RBS to GeSi profile conversion with nawk

The following nawk script was written to effect direct conversion of Rutherford Backscattering Spectroscopy data of GeSi films with depth-dependent Ge fraction into profiles of the Ge fraction as a function of sub-surface depth. nawk is a commonly available stream-oriented interpreter for UNIX systems with a C-like command syntax.


##############################################################
# RBS_to_SiGe_profile:
# 
#   This program attempts to extract material
# profile information from SiGe structure RBS
# data.
# 
# 31May-2June 1993   Dan Connelly djconnel@leland.stanford.edu
# 17Jan1997: changed to L'Ecuyer scattering cross-section; previous model was
#            flawed.
# last modified: 03Feb1997
# 
# 
# Function notation:
# 
# function fname(x1,x2,....xn,\
#                y1,y2,....ym)
# 
# x1,x2,...xn : expected arguments
# y1,y2,...yn : local variables -- will be ignored if passed
# 
#
##############################################################

BEGIN{
  #step down NR to start it at zero
  NR--;

  # set options
  abort_on_zero_Ge_flag = 0;
}

# skip empty lines
(NF==0){
  NR--;
  next;
}

(NR==0){
  # material array index tags
  Si    = 1;          #tag
  Ge    = 2;          #tag

  # material constants
  pi    = atan2(0,-1);
  M[Si] = 28.08;      #atomic weight of Si in amu -- averaged over isotopes
  M[Ge] = 72.59;      #atomic weight of Ge in amu -- averaged over isotopes
  M_He  =  4;         #mass of alpha particles
  Z[Si] = 14;         #nuclear charge on Si
  Z[Ge] = 32;         #nuclear charge on Ge
  Z_He  =  2;         #nuclear charge on alpha particle beam
  N[Si] = 4.978e10;   #number density, um^-3, from RUMP, 1.660440e-24 g/amu ("units")
  N[Ge] = 4.429e10;
  D[Si] = 2.32;       #calculated from M,N -- g/cm^3
  D[Ge] = 5.338;

  e2 = 1/137;          #square of electron charge, in h-bar c = 0

  # correction factors to electronic stopping power and scattering cross-section
  if (dEdr_Si)
    dEdr_factor[Si] = dEdr_Si;
  else
    dEdr_factor[Si] = 1;
  if (dEdr_Ge)
    dEdr_factor[Ge] = dEdr_Ge;
  else
    dEdr_factor[Ge] = 1;
  if (dsdo_Si)
    dsdo_factor[Si] = dsdo_Si;
  else
    dsdo_factor[Si] = 1;
  if (dsdo_Ge)
    dsdo_factor[Ge] = dsdo_Ge;
  else
    dsdo_factor[Ge] = 1;

  # Temp: The following lines are for temporary use only -- they
  # should be user-settable.
  E_He        = 2.20; #energy of alpha particle beam


  # for tilt spectrum -- detecter, wafer normal, and incoming beam are coplanar
  # constraint: theta_deg = 180 - (theta_out - theta_in)
  if (tiltflag) {
    theta_gauge_deg = tiltangle;   # reading on the guage
    theta_in_deg  = theta;         # incoming beam, off-normal
    theta_out_deg = 181.5 - theta_gauge_deg  + theta_in_deg;  # outgoing beam, off-normal
    theta_deg     = 180 + theta_in_deg - theta_out_deg;   # scattering angle
    }

  # for backscattering data -- detecter, wafer normal, and incoming beam form
  # constraint on angles: cos[theta_out] = cos[theta]_in * cos[theta]
  # right angle
  if (bsflag){
    theta_in_deg  = -6;      # incoming beam, off-normal
    theta_deg     = 170.0;   # outgoing beam, off-normal
    theta_out_deg = 10.7;      # scattering angle
    }

  theta_in      = theta_in_deg  * pi / 180;
  theta_out     = theta_out_deg * pi / 180;
  theta         = theta_deg * pi / 180;
  sec_in        = 1/cos(theta_in);
  sec_out       = 1/cos(theta_out);

 
  if (abs(sin(theta/2))<1e-10) {
    system("echo_stderr \"ERROR -- Backscattering at 180deg not allowed -- aborting.\"");
    exit;
  }

  # calculate theta-independent screening factor
  calculate_dsdo_parameters(Si);
  calculate_dsdo_parameters(Ge);

  # determine column labels
  col_chan = 0;
  col_y_Si = 0;
  col_s_Si = 0;
  col_y_Ge = 0;
  col_s_Ge = 0;

  for (i=0;i++<NF;)
    if ($i ~ "^chan.*")
      col_chan = i;
    else if ($i == "y_Si")
      col_y_Si = i;
    else if ($i == "s_Si")
      col_s_Si = i;
    else if ($i == "y_Ge")
      col_y_Ge = i;
    else if ($i == "s_Ge")
      col_s_Ge = i;

  errorcount = 0;
  if (col_chan == 0) {
    system("echo_stderr \"ERROR : Column for channel number not found.\"");
    errorcount++;
  }
  if (col_y_Si == 0) {
    system("echo_stderr \"ERROR : Column for Si counts not found.\"");
    errorcount++;
  }
  if (col_y_Ge == 0) {
    system("echo_stderr \"ERROR : Column for Ge counts not found.\"");
    errorcount++;
  }
  if (errorcount) {
    system("echo_stderr " sprintf("Fatal error count = %d\n",errorcount));
    system("echo_stderr \"Aborting...\"");
    exit;
  }
  next;
}

#read in data lines
{
  y_Si[$col_chan] = $col_y_Si;
  if (col_s_Si)
    s_Si[$col_chan] = $col_s_Si;
  else
    s_Si[$col_chan] = sqrt(abs($col_y_Si)+1);
  y_Ge[$col_chan] = $col_y_Ge;
  if (col_s_Ge)
    s_Ge[$col_chan] = $col_s_Ge;
  else
    s_Ge[$col_chan] = sqrt(abs($col_y_Ge)+1);
}


# process the data
END{
  # calculate the Si and Ge edge, or used pre-specified values

  cSi = find_surface_channel(y_Si);
  if (chan_si != ""){
    system("echo_stderr \"Si surface channel extraction overridden:\"" chan_si);
    chanmax[Si]=chan_si;
  }
  else{
    if (cSi == 0)
      system("echo_stderr ERROR : Si surface channel not found.");
    chanmax[Si] = cSi;
  }

  cGe = find_surface_channel(y_Ge);
  if (chan_ge != ""){
    system("echo_stderr \"Ge surface channel extraction overridden:\"" chan_ge);
    chanmax[Ge]=chan_ge;
  }
  else{
    if (cGe == 0)
      system("echo_stderr ERROR : Ge surface channel not found.");
    chanmax[Ge] = cGe;
  }

  if ((chanmax[Si] == 0) || (chanmax[Ge] == 0))
    exit;


  # find the starting values
  do_surface_calculations();

  # output the column labels for the output table
  print_column_labels();

  # do the calculations vs depth
  while ((chan[Si]>=0)&&(chan[Ge]>=0)){
    x = add_depth_point();
    if ((abort_on_zero_Ge_flag && (x == 0))||((1*z_max > 0)&&((z_Si>z_max)||(z_Ge>z_max))))
      break;
  }
}

#########################################
# FIND_SURFACE_CHANNEL
#
# Try to determine the channels marking the surface counts
# of Ge, Si.
#

function find_surface_channel(y,\
  xx,yy,dyy,yymax,i,imin,imax,c_edge,dx0,dx1,dx2)
{
  c_edge = 0;
  yymax = "";
  for (i in y) {
    norm = 3;
    yy[i] = 3*y[i];
    xx[i] = 3*i;
    if (y[i-2] != ""){
      yy[i] += y[i-2];
      xx[i] += i-2;
      norm += 1;
    }
    if (y[i-1] != ""){
      yy[i] += 2*y[i-1];
      xx[i] += 2*(i-1);
      norm += 2;
    }
    if (y[i+1] != ""){
      yy[i] += 2*(y[i+1]);
      xx[i] += 2*(i+1);
      norm += 2;
    }
    if (y[i+2] != ""){
      yy[i] += y[i+2];
      xx[i] += i+2;
      norm += 1;
    }
    xx[i]/=norm;
    yy[i]/=norm;

    if ((yymax == "") || (yy[i] > yymax))
      yymax = yy[i];

  }

  # calculate approximation to dy/dx
  for (i in y)
    if ((1*yy[i] != 0) &&\
	(xx[i+1] != "") &&\
	(xx[i-1] != "") &&\
	(xx[i] != "") &&\
	((dx2=xx[i+1]-xx[i])!=0) &&\
	((dx0=xx[i]-xx[i-1])!=0) &&\
	((dx1=xx[i+1]-xx[i-1])!=0))
      dyy[i] = ((yy[i+1]-yy[i])*dx0/dx2 +\
		(yy[i]-yy[i-1])*dx2/dx0)/\
	          dx1;

  imax = "";
  imin = "";
  for (i in y)
    if (y[i] != "") {
      if ((imax == "") || (int(i) > int(imax)))
	imax = int(i);
      if ((imin == "") || (int(i) < int(imin)))
	imin = int(i);
    }

  if (imax > imin+3)
  for (i = imax-3; i >= imin; i--)
    if (dyy[i] != "")
      if (yy[i] > yymax/10)
	if ((dyy[i] > dyy[i+1]) && (dyy[i] > dyy[i+2]) && (dyy[i] > dyy[i+3])) {
	  system("echo_stderr Edge found at channel " (c_edge = int(i+3.5)));
	  break;
	}
    
  return c_edge;
}
       


##########################################
# DO_SURFACE_CALCULATIONS
#
# calculate parameters at surface
#
# Global arrays initialized here:
#   z_array[Ge,0]  : the depth at the surface = 0
#   z_array[Si,0]  : the depth at the surface = 0
#   x_array[Ge,0]  : the alloy content at the surface
#   x_array[Si,0]  : the alloy content at the surface
#   dEindz[Ge,0]   : the electronic stopping rate at the surface
#   dEindz[Si,0]   : the electronic stopping rate at the surface
#
# Global variables initialized here:
#   Echan0    : energy of channel zero
#   Echan1    : energy of channel 1 - Echan0
#   chan[Si]  : channel number of next point under consideration
#               in silicon data
#   chan[Ge]  : channel number of next point under consideration
#               in germanium data
#

function do_surface_calculations(\
				 xmean,xmax,xmin){

  # print comment line to be stripped with stripcomments, for example
  printf("/************************************\n");

  # print simulation parameters
  printf "%-31s= %s amu\n","M[Si]",M[Si];
  printf "%-31s= %s amu\n","M[Ge]",M[Ge];
  printf "%-31s= %s amu\n","M[He]",M_He;
  printf "%-31s= %s\n","Z[Si]",Z[Si];
  printf "%-31s= %s\n","Z[Ge]",Z[Ge];
  printf "%-31s= %s\n","Z[He]",Z_He;
  printf "%-31s= %s g/cm^3\n","Density[Si]",D[Si];
  printf "%-31s= %s g/cm^3\n","Density[Ge]",D[Ge];
  printf "%-31s= %s MeV\n","He energy",E_He;
  printf "%-31s= %s\n","Si surface channel",chanmax[Si];
  printf "%-31s= %s\n","Ge surface channel",chanmax[Ge];
  printf "%-31s= ","Tilt/Backscatter mode";
  if (tiltflag)
    print "tilt";
  else if (bsflag)
    print "backscatter";
  else
    print "???";
  if (tiltflag)
    printf "%-50s= %s deg\n","Angle reading on gauge",theta_gauge_deg;
  printf "%-50s= %s deg\n","Angle of incoming beam to wafer normal",theta_in_deg;
  printf "%-50s= %s deg\n","Angle of detected beam to wafer normal",theta_out_deg;
  printf "%-50s= %s deg\n","Scattering angle: incoming to outgoing beams",theta_deg;

  # print header comments
  printf "%-50s= %s MeV\n","Recoil energy of He off surface Si",\
    Emax[Si] = E_He * (elastic_factor[Si] = elasticity(M[Si],theta));
  printf "%-50s= %s MeV\n","Recoil energy of He off surface Ge",\
    Emax[Ge] = E_He * (elastic_factor[Ge] = elasticity(M[Ge],theta));
  printf "%-31s= %g MeV\n","Difference",Emax[Ge]-Emax[Si];
  printf "%-31s= %s\n","Elasticity He off Si",elastic_factor[Si];
  printf "%-31s= %s\n","Elasticity He off Ge",elastic_factor[Ge];
  printf "%-31s= %g MeV\n","Energy of channel 0",\
    Echan0=(chanmax[Ge]*Emax[Si] - chanmax[Si]*Emax[Ge])/(chanmax[Ge]-chanmax[Si]);
  printf "%-31s= %g MeV\n","E[channel 1] - E[channel0]",
    Echan1=(Emax[Si]-Emax[Ge])/(chanmax[Si]-chanmax[Ge]);

  printf "%-31s= %g\n","dE/dr correction factor for Si",dEdr_factor[Si];
  printf "%-31s= %g\n","dE/dr correction factor for Ge",dEdr_factor[Ge];

  printf("dE/dr in Si at E_He            = %g MeV/um\n", temp0=dEdr(0,E_He));
  printf("dE/dr in Ge at E_He            = %g MeV/um\n", temp1=dEdr(1,E_He));
  printf("ratio dE/dr at E_He Ge/Si      = %g\n", temp1/temp0);
  printf("dE/dr in Si at Si edge         = %g MeV/um\n", temp0=dEdr(0,Emax[Si]));
  printf("dE/dr in Ge at Si edge         = %g MeV/um\n", temp1=dEdr(1,Emax[Si]));
  printf("ratio dE/dr at Si edge Ge/Si   = %g\n", temp1/temp0);
  printf("dE/dr in Si at Ge edge         = %g MeV/um\n", temp0=dEdr(0,Emax[Ge]));
  printf("dE/dr in Ge at Ge edge         = %g MeV/um\n", temp1=dEdr(1,Emax[Ge]));
  printf("ratio dE/dr at Ge edge Ge/Si   = %g\n", temp1/temp0);
  printf("dE/dr in Si at chan 0          = %g MeV/um\n", temp0=dEdr(0,Echan0));
  printf("dE/dr in Ge at chan 0          = %g MeV/um\n", temp1=dEdr(1,Echan0));
  printf("ratio dE/dr at chan 0 Ge/Si    = %g\n", temp1/temp0);

  printf("d Sigma_Si/d Omega at E_He     = %g MeV^-2\n",
	 dsdo(Si,E_He));
  printf("d Sigma_Si/d Omega at chan 0   = %g MeV^-2\n",
	 dsdo(Si,Echan0));
  printf("d Sigma_Ge/d Omega at E_He     = %g MeV^-2\n",
	 dsdo(Ge,E_He));
  printf("d Sigma_Ge/d Omega at chan 0   = %g MeV^-2\n",
	 dsdo(Ge,Echan0));
  printf("d Sigma_Ge/d Sigma_Si at E_He  = %g\n",
	 dsdo(Ge,E_He)/dsdo(Si,E_He));
  printf("d Sigma_Ge/d Sigma_Si at chan 0= %g\n",
	 dsdo(Ge,Echan0)/dsdo(Si,Echan0));


  printf("Count at surface : Si(channel %d)= %d, Ge(channel %d)= %d\n",
	 chanmax[Si],
	 y_Si[chanmax[Si]],
	 chanmax[Ge],
	 y_Ge[chanmax[Ge]]);

  printf("Count 5 channels lower : Si(channel %d)= %d, Ge(channel %d)= %d\n",
	 chanmax[Si]-5,
	 y_Si[chanmax[Si]-5],
	 chanmax[Ge]-5,
	 y_Ge[chanmax[Ge]-5]);

  xmean= find_surface_x(y_Si[chanmax[Si]],y_Ge[chanmax[Ge]]);
  d_array[Si,0] =\
  d_array[Ge,0] = net_density;   /* set by routine */

  xmax = find_surface_x(y_Si[chanmax[Si]]-s_Si[chanmax[Si]],y_Ge[chanmax[Ge]]+s_Ge[chanmax[Ge]]);
  xmin = find_surface_x(y_Si[chanmax[Si]]+s_Si[chanmax[Si]],y_Ge[chanmax[Ge]]-s_Ge[chanmax[Ge]]);

  printf("x at surface       = %g (sqrt2-sigma range= %g,%g)\n",xmean,xmin,xmax);
  printf("sigma of surface x = %g\n",(xmax-xmin)/sqrt(8));

  # print end-of-comment line
  printf("************************************/\n");

  # initialize global arrays
  z_array[Si,0]  = \
  z_array[Ge,0]  = 0;
  x_array[Si,0]  = \
  x_array[Ge,0]  = xmean;
  Ein[Si,0]      = \
  Ein[Ge,0]      = E_He;
  dEindz[Si,0]   = \
  dEindz[Ge,0]   = dEdr(xmean,E_He)*sec_in;

  #initialize global variables
  npoints[Ge]  = 0;        # there are 0 new points in the depth and energy arrays
  npoints[Si]  = 0;        # there are 0 new points in the depth and energy arrays
  chan[Si] = chanmax[Si]   # we looked last at this point in Si data
  chan[Ge] = chanmax[Ge]   # we looked last at this point in Ge data
  ns_last  = 0;            # next Si point to be printed out

  #
  # convert net density to something proportional to atomic density
  #  atoms/volume =
  #        (dNcounts/dEout) * (dEout/dz)                                 1
  #   --------------------------------------------  -----------------------------------------
  #    (dr/dz = sec(theta_in)) * (d sigma/d omega)   (omega_detector) * (N_He in per channel)
  #
  #  the first is the thing we calculate.
  #
  # units: (counts/MeV) (MeV/um)
  #        --------------------- =  um^-3 Str-counts
  #              um^2/Str            
  #
  # Note: for units to work e^2 must be set appropriately and cross-section formula
  #      needs to be adjusted gaussian -> SI

  density_factor = 1/(sec_in*Echan1);

}

########################################
# FIND_SURFACE_X
# this routine tries to determine the value
# of x at the surface
#

function find_surface_x(ySi,yGe,\
			xGe,xSi,zGe,zSi,dedz,x,i,imax) {

  # variables:
  # ----------
  #   ySi,yGe : counts in channel
  #   zSi,zGe : counts in channel over differential scattering cross section
  #   xSi,xGe : zSi,zGe divided by differential change in round-trip energy
  #             change with depth
  #   dedz    : rate of change of energy of beam coming out with respect to
  #             z-axis depth -- note this must be scaled by the |sec| of the
  #             angle to convert from de/d(ion path length)
  # return value: ge mole fraction
  # net_density : number proportional to the observed net particle density --
  #             global variable set by this routine

  # guess at x_surface and then iterate
  zGe = yGe / \
    dsdo(Ge,E_He);
  x = zGe / \
    ((zSi = ySi / \
      dsdo(Si,E_He)) + \
     zGe);

  for (i=(imax=3);i--;) {
    dedz = dEdr(x,E_He)*sec_out;

    # net dEdz where x is the depth at which the backscatter occurs:
    #   (a) electron damping going in the extra dx +
    #   (b) change in inelastic component of scatter at x+dx vs x +
    #   (c) electron damping going out dx from the scatter site +
    #   (d) change in electron going out from x to zero
    # At the surface (d) is zero
    #
    # note the change in the scattering energy, since the elasticity
    # is energy-independent, is (elasticity-1)(dE/dx going in) (less energy
    # lost in the collision the deeper one goes).
    # This adds to dE/dx going in and out to yield
    #
    # net dE/dx = ((elasticity)(dE/dx going in)) + dE/dx going out

    xGe = zGe*(elastic_factor[Ge]*dedz + dEdr(x,Emax[Ge])*sec_in);
    xSi = zSi*(elastic_factor[Si]*dedz + dEdr(x,Emax[Si])*sec_in);
    x = xGe/ \
      net_density=(xSi+xGe);
  }
  return x;
}

#################################################
# ADD_DEPTH_POINT
# This routine extends the array of (x,E) values
# for alphas bouncing off Ge atoms.  It uses the
# array of x versus depth z...
#
# global arrays:
#   z_array[Ge,n]  : depth of nth data point of Ge
#   z_array[Si,n]  : depth of nth data point of Si
#   x_array[Ge,n]  : x value at nth data point of Ge
#   x_array[Si,n]  : x value at nth data point of Si
#   Ein[Ge,n]      : energy of alphas at the grid point for Si 
#   Ein[Si,n]      : energy of alphas at the grid point for Ge
#   dEindz[Ge,n]   : damping rate of incoming alphas at the grid point of Ge
#   dEindz[Si,n]   : damping rate of incoming alphas at the grid point of Si
#
# global variables:
#   npoints[Ge]    : number of points in the depth array and x_array of Ge
#   npoints[Si]    : number of points in the depth array and x_array of Si
#   chan[Ge]       : this is stepped down as a new data point is added
#   chan[Si]       : channel of Si channel under consideration -- channel
#                    is chosen to match germanium channel as closely as
#                    possible
#
# local arrays
#   Eout[Si or Ge,n]   : energy of alphas at the grid point on their way out
#                        assuming they reach the surface at the energy of interest
#   dEoutdz[Si or Ge,n]: rate of energy loss of alphas on their way out assuming
#                        they reach the surface at the energy of interest
#

function add_depth_point(\
                         i,n,delta_z,x,E,Eout,dEoutdz,dedz,ns,ng,deltaz1,deltaz2,deltaz,zg1,zg2,zs1,zs2) {

  # step down Ge channel number and determine the energy of that channel
  E = channel_to_energy( \
      --chan[Ge] \
      );

  if (chan[Ge]<0)
    return 0;

  Eout[Ge,0]    = E;                             # particle reaches surface at chosen energy from Ge impact
  dEoutdz[Ge,0] = dEdr(x_array[Ge,0],E)*sec_out; # rate at which particle is losing energy at the surface

  for(n=0;n++<npoints[Ge];) {
    delta_z   = z_array[Ge,n]-z_array[Ge,n-1];          # change in depth over the interval

    dE = delta_z*dEoutdz[Ge,n-1];
    for(i=3;i--;){
      dedz = dEdr(x_array[Ge,n],Eout[Ge,n-1]+dE)*sec_out;
      dE = delta_z * (dedz + dEoutdz[Ge,n-1])/2;
    }
    dEoutdz[Ge,n] = dedz;
    Eout[Ge,n] = Eout[Ge,n-1] + dE;
  }
  n--;

  # extrapolate x value of new point --
  # we want Eout(z) = Ein(z) * elasticity
  #
  # approximate
  #   Eout  = elasticity Ein = Eout0 + delta_z dEout/dz
  #                      Ein = Ein0  - delta_z dEin/dz
  #
  # substitute for Ein: 
  # elasticity (Ein0 - delta_z dEin/dz) = Eout0 + delta_z dEout/dz
  #
  # combine terms:
  # delta_z (elasticity dEin/dz + dEout/dz) = elasticity Ein0 - Eout0
  #
  # divide:
  # delta_z = (elasticity Ein0 - Eout0) / (elasticity dEin/dz + dEout/dz) 

  z_array[Ge,n+1] = z_array[Ge,n] + \
    (elastic_factor[Ge]*Ein[Ge,n] - Eout[Ge,n]) / (elastic_factor[Ge]*dEindz[Ge,n] + dEoutdz[Ge,n]);

  # step until we get to a silicon point past the last germanum one.
  while(z_array[Si,npoints[Si]] <= z_array[Ge,npoints[Ge]]) {

    # if we run out of silicon data call it quits, otherwise step down silicon grid
    E = channel_to_energy( \
        --chan[Si]\
        );

    if (chan[Si] < 0)
      return 0;

    Eout[Si,0]    = E;                             # particle reaches surface at chosen energy from Ge impact
    dEoutdz[Si,0] = dEdr(x_array[Si,0],E)*sec_out; # rate at which particle is losing energy at the surface

    for(n=0;n++<npoints[Si];) {
      delta_z = z_array[Si,n]-z_array[Si,n-1];        # change in depth over the interval
	  
      dE = delta_z*dEoutdz[Si,n-1];

      for(i=3;i--;){
	dedz = dEdr(x_array[Si,n],Eout[Si,n-1]+dE)*sec_out;
	dE = delta_z * (dedz + dEoutdz[Si,n-1])/2;
      }
      dEoutdz[Si,n] = dedz;
      Eout[Si,n] = Eout[Si,n-1] + dE;
    }
    n--;
    ns = ++npoints[Si];
    ng = npoints[Ge];

    # extrapolate the depth, as with the germanium data
    zs2 = \
      z_array[Si,ns] = \
	(zs1 = z_array[Si,n]) + \
	  (elastic_factor[Si]*Ein[Si,n] - Eout[Si,n]) / (elastic_factor[Si]*dEindz[Si,n] + dEoutdz[Si,n]);

    if (ng){
      zg1 = z_array[Ge,ng-1];
      zg2 = z_array[Ge,ng];

      # if it exists, calc d_array for straddled Si point
      if (ns>1){
        # interpolate calc net density for straddled Si point
        deltaz = \
	  (deltaz1 = (zs1 - zg1)) + \
	    (deltaz2 = (zg2 - zs1));

	  d_array[Si,n] = \
	    (deltaz2 * d_array[Ge,ng-1] + \
	     deltaz1 * d_array[Ge,ng]) / \
	       deltaz;
      }

      # determine x and Ein for the new Si point

      deltaz = \
	(deltaz1 = (zs2 - zg1)) + \
	  (deltaz2 = (zg2 - zs2));
      
      x_array[Si,ns] = \
	(deltaz2 * x_array[Ge,ng-1] + \
	 deltaz1 * x_array[Ge,ng]) / \
	   deltaz;

      # don't bother calculating d_array[Si,ns], which isn't used.

      Ein[Si,ns]=Ein[Si,n]+dEindz[Si,n]*(zs2-zs1);
      for (i=2;i--;){
	dEindz[Si,ns] = sec_in*dEdr(x_array[Si,ns],Ein[Si,ns]);
	Ein[Si,ns] = Ein[Si,n] - (dEindz[Si,n]+dEindz[Si,ns])*(zs2-zs1)/2;
      }
    }
    else {
      dEindz[Si,ns] = dEindz[Si,ns-1];
      for (i=2;i--;)
	dEindz[Si,ns] = sec_in * dEdr(\
	  (x_array[Si,ns] = x_array[Si,0]),\
          (Ein[Si,ns] = Ein[Si,ns-1] +(z_array[Si,ns-1]-z_array[Si,ns])*(dEindz[Si,ns-1]+dEindz[Si,ns])/2)\
	  );
    }

  }

  #################################  
  # Now we have the last two Si points straddling the last Ge point.  We can extrapolate
  # the Si counts to match the depth and calculate the atomic germanium fraction.
  # we can do the same for the second-to-last Si point using the straddling germanium
  # data
  #
  # variables:
  # ----------
  #   ySi,yGe : counts in channel
  #   zSi,zGe : counts in channel over differential scattering cross section
  #   xSi,xGe : zSi,zGe divided by differential change in round-trip energy
  #             change with depth
  

  #############################################################
  # Calculate results for new previous then for current Ge point

  ns = npoints[Si];

  for (ng=max(npoints[Ge],1);ng<=npoints[Ge]+1;ng++) {
    dE = \
      (dEindz[Ge,ng] = dEindz[Ge,ng-1]) * \
	(dz_Ge = z_array[Ge,ng]-z_array[Ge,ng-1]);
    
    for (i=2;i--;){
      #calculate Ein
      Ein[Ge,ng]= Ein[Ge,ng-1] - dE;
      
      deltaz = \
	(deltaz1 = (z_array[Ge,ng] - z_array[Si,ns-1])) +\
	  (deltaz2 = (z_array[Si,ns] - z_array[Ge,ng]));
      
      zGe = \
	(yGe = y_Ge[chanmax[Ge]-ng]) /\
	  dsdo(Ge,Ein[Ge,ng]);
      zSi = \
	(ySi = ((y_Si[chanmax[Si]-ns]  * deltaz1 + \
		 y_Si[chanmax[Si]-ns+1]* deltaz2)/ \
		deltaz)) / \
		  dsdo(Si, \
		       (EinSi = \
			(Ein[Si,ns]   * deltaz1 + \
			 Ein[Si,ns-1] * deltaz2)/ \
			deltaz));
      
      # numerically determine dedz
      dedz_Si = Echan1 / (z_array[Si,ns] - z_array[Si,ns-1]);
      dedz_Ge = Echan1 / (z_array[Ge,ng] - z_array[Ge,ng-1]);

      x = x_array[Ge,ng] = zGe*dedz_Ge / \
	d_array[Ge,ng] = (zGe*dedz_Ge + zSi*dedz_Si);
      dEindz[Ge,ng] = dEdr(x,Ein[Ge,ng])*sec_in;
      dE = (dEindz[Ge,ng-1] + dEindz[Ge,ng]) * dz_Ge / 2;
    }

    # revise all Si points affected by the new Ge point
    for (n=ns; n && (z_array[Si,n]>z_array[Ge,ng-1]); n--);
    while(n++<ns) {
      zs  = z_array[Si,n];
      zg1 = z_array[Ge,ng-1];
      zg2 = z_array[Ge,ng];
      
      # interpolate x and calc Ein for the straddled Si point based on new Ge point data
      deltaz = \
	(deltaz1 = (zs - zg1)) + \
	  (deltaz2 = (zg2 - zs));
      
      x_array[Si,n] = \
	(deltaz2 * x_array[Ge,ng-1] + \
	 deltaz1 * x_array[Ge,ng]) / \
	   deltaz;
	   
      for (i=2;i--;){
	dEindz[Si,n] = sec_in*dEdr(x_array[Si,n],Ein[Si,n]);
	Ein[Si,n] = Ein[Si,n-1] - (dEindz[Si,n-1]+dEindz[Si,n])*(z_array[Si,n]-z_array[Si,n-1])/2;
      }
    }
    
    

  }
  npoints[Ge]++;
  ng--;


  #######################################
  # output the data point
  #
  i=chanmax[Ge]-ng+1;
  print "Ge",\
    i,\
    ng-1,
    y_Si[i],\
    y_Ge[i],\
    y_Ge[i]+y_Si[i],\
    (z_Ge = z_array[Ge,ng-1]),\
    (x_last = x_array[Ge,ng-1]),\
    Ein[Ge,ng-1],\
    Eout[Ge,0],\
    dEindz[Ge,ng-1],\
    dedz_Ge,\
    d_array[Ge,ng-1]*density_factor;

  for (n=ns_last; n<ns; n++) {
    i=chanmax[Si]-ns+1;
    print "Si",\
      i,\
      ns-1,
      y_Si[i],\
      y_Ge[i],\
      y_Ge[i]+y_Si[i],\
      (z_Si = z_array[Si,n]),\
      (x_last = x_array[Si,n]),\
      Ein[Si,n],\
      Eout[Si,0],\
      dEindz[Si,n],\
      dedz_Si,\
      d_array[Si,n]*density_factor;

   }
  ns_last = ns;
 
  return x_last;

}

##################################################
# PRINT_COLUMN_LABELS
#
function print_column_labels(){
print\
  "atom",\
  "chan",\
  "chan_rel",\
  "counts_Si",\
  "counts_Ge",\
  "counts",\
  "depth",\
  "x",\
  "Escatter",\
  "Eout",\
  "dEscatter/dz",\
  "dEout/dz",\
  "net_density"
}

##################################################
# SQUARE : x*x
#
function square(x){
  return x*x;
}

##################################################
# ABS
#
function abs(x){
  if (x<0)
    return -x;
  else
    return x;
}

##################################################
# MIN
#
function max(x,y){
  if (x<y)
    return y;
  else
    return x;
}

##################################################
# CHANNEL_TO_ENERGY : convert channel number
# to energy.  Expect there is a linear relation
# and the coefficients have been set
#
function channel_to_energy(channel){
  return Echan0 + channel*Echan1;
}

##################################################
# DSDO : calculate the differential cross section of
# the particular element to alpha particles at the given
# angle for the given target mass at the given energy
# with the given nuclear charge of the target.
#
function dsdo(species,E,\
              xcom,xlab,Ecom){
  xcom = (e2 * Z_He * Z[species] / (4 * E * stheta2))^2;
  xlab = xcom * (r1mr2s2 + ctheta)^2/r1mr2s2;
  Ecom = E*Efactor
  return (xlab * (1 - ((Z[species]*Z_He)^(4/3))*49e-6/Ecom));
  
}
#################################################
# CALCULATE_DSDO_PARAMETERS:
# set global variables appropriate for calculation
# of scattering cross-section
#
# These parameters are based on the L'Ecuyer
# lab-frame approximation to the screening-corrected
# Rutherford Backscattering formula
#
function calculate_dsdo_parameters(species){
  rho     = M_He/M[species];
  Efactor = (1 + rho)^-2;
  stheta2 = sin(theta)^2;
  ctheta  = cos(theta);
  r1mr2s2 = sqrt(1 - rho^2*stheta2);
}

#################################################
# ELASTICITY
# return the collision elasticity factor for a given
# target versus alpha particles\
#
function elasticity(M,theta,\
                    Mcos,Msum){
  Mcos = M_He*cos(theta) / \
   (Msum = M + M_He);

  return square(Mcos + sqrt(Mcos*Mcos + (M-M_He)/Msum));
}
  

##################################################
# STOPPING_POWER : return the stopping power
# at the given energy.
#

function stopping_power(species,e,\
                        slow, shigh){

  if (species==Ge) {
   slow  = 143.67 * e^0.466;
   shigh = (79.24/e)*log(1 + (1.185/e) + (7.993*e));
  }
  else if (species==Si) {
   slow  = 187.16 * e^0.65;
   shigh = (49.34/e)*log(1 + (1.788/e) + (4.133*e));
  }
  else {
   print "ERROR : STOPPING POWER CALLED WITH ILLEGAL SPECIES";
   return 0;
  }

  return 10^-13*N[species]/(1/slow + 1/shigh);
}

##################################################
# dEdr: Find net stopping power for alloy of a given
# composition at the given energy assuming a linear
# superposition of stopping powers.
#
# Note : this returns the change in E with respect to
#    r, the path length, not z, the depth.  Multiply
#    by abs(sec(theta_{in,out})) to get dEdz.
#
function dEdr(x,E) {

  # assume Vegards law applies to number density instead of
  # lattice constant.

  return \
    x     * stopping_power(Ge,E) * dEdr_factor[Ge] + \
    (1-x) * stopping_power(Si,E) * dEdr_factor[Si];
}

The following shell script passes the nawk script to nawk:

#! /bin/csh -f
#
# This code acts as front-end shell interface for the
# nawk script RBS_to_SiGe_profile.nawk.
# 
# written by Daniel Connelly, Stanford University
# last modified: 03 Feb 1997
#
set programname = $0
@ verboseflag = 0;
@ tiltflag = 0;
@ bsflag   = 1;
set tiltangle = 100;
set theta_default = -6;
set theta = $theta_default;
set bsangle   = ;
set dEdr_Si = ""
set dEdr_Ge = ""
set dsdo_Si = ""
set dsdo_Ge = ""
set z_max   = ""
set chan_si = ""
set chan_ge = ""

while ($#argv)
  set s0 = $1
  set s = `echo $1 | /u2/djconnel/bin/toupper`
  if ($s == "-DEDR_SI") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      dEdr_Si = $1
    else
      set dEdr_Si = ""
    endif
  else if ($s == "-DEDR_GE") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set dEdr_Ge = $1
    else
      set dEdr_Ge = ""
    endif
  else if ($s == "-DSDO_SI") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set dsdo_Si = $1
    else
      set dsdo_Si = ""
    endif
  else if ($s == "-DSDO_GE") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set dsdo_Ge = $1
    else
      set dsdo_Ge = ""
    endif
  else if ($s == "-Z_MAX") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set z_max = $1
    else
      set z_max = ""
    endif
  else if ($s == "-CHAN_SI") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set chan_si = $1
    else
      set chan_si = ""
    endif
  else if ($s == "-CHAN_GE") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set chan_ge = $1
    else
      set chan_ge = ""
    endif
  else if ($s == "-TILT") then
    @ tiltflag = 1;
    @ bsflag   = 0;
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set tiltangle = $1
    endif
  else if ($s == "-BS") then
    @ bsflag = 1;
    @ tileflag   = 0;
  else if ($s == "-THETA") then
    if (($#argv > 1)&&({(is_valid_number $2)})) then
      shift
      set theta = $1
    else
      set theta = $theta_default
    endif
  else if ($s == "-V") then
    echo_stderr $programname ": verbose flag on"
    @ verboseflag = 1
  else if ($s == "+V") then
    echo_stderr $programname ": verbose flag off"
    @ verboseflag = 0
  else if (($s == "-H") || ($s == "-HELP")) then

    more << EOF
usage: RBS_to_SiGe [options]
options (case-insensitive):
  -dsdo_Ge : set a scaling factor for the scattering cross-section calculation
	for Ge nucleii.
  -dsdo_Si : set a scaling factor for the scattering cross-section calculation
	for Si nucleii.
  -dEdr_Ge : set a scaling factor for the electronic scattering energy dissipation
	rate calculation for Ge nucleii.
  -dEdr_Si : set a scaling factor for the electronic scattering energy dissipation
	rate calculation for Si nucleii.
  -chan_Si : don't rely on auto-calibration -- surface Si recoils are detected at
	channel 
  -chan_Ge : don't rely on auto-calibration -- surface Ge recoils are detected at
	channel 
  -z_max : abort the calculation when a depth  is reached by any of the
	data points
  -bs : data represents a backscatter spectrum
  -tilt [] : data represents a tilt-angle spectrum; specify the optional "meter"
        reading of the angle in degrees or omit it to use the default.
  -theta [] : the angle of the incoming beam relative to normal.  For backscatter
        detector, sign of angle is irrelevent (right angle looking along wafer normal).
        For tilt-angle detector, sign should be opposite of -tilt for beam passing through
        wafer normal.  Default is $theta_default.
  -v:   verbose flag -- list debug info to stderr
  -h:   help

  Note: In the above,  indicates the next argument should be a real number
	to be assigned to the applicable parameter.  If it is not, the default
	value will be used.
EOF
    exit
  else
    echo_stderr $programname ": skipping unrecognized option" $s0
  endif
  shift
end
if ($verboseflag == 1) then
  if ($dEdr_Si != "")\
    echo_stderr $programname ": dEdr_Si =" "$dEdr_Si"
  if ($dEdr_Ge != "")\
    echo_stderr $programname ": dEdr_Ge =" "$dEdr_Ge"
  if ($dsdo_Si != "")\
    echo_stderr $programname ": dsdo_Si =" "$dsdo_Si"
  if ($dsdo_Ge != "")\
    echo_stderr $programname ": dsdo_Ge =" "$dsdo_Ge"
  if ($z_max != "")\
    echo_stderr $programname ": z_max   =" "$z_max"
  if ($bsflag)\
    echo_stderr $programname ": Backscatter spectrum is in effect"
  if ($tiltflag)\
    echo_stderr $programname ": Tilt-Angle spectrum is in effect (reading = "$tiltangle")";
endif  
nawk\
      -f /u13/djconnel/bin/RBS_to_SiGe_profile.nawk\
      dsdo_Si="$dsdo_Si" dsdo_Ge="$dsdo_Ge" dEdr_Si="$dEdr_Si" dEdr_Ge="$dEdr_Ge" z_max="$z_max"\
      chan_si="$chan_si" chan_ge="$chan_ge" bsflag="$bsflag" tiltflag="$tiltflag" tiltangle="$tiltangle"\
      theta="$theta"