Author Topic: Scripting DTSA-II Monte Carlo simulation several incident angles values  (Read 86 times)

jpcruzcaM

  • Student
  • *
  • Posts: 1
Hello!

I am studying a set of samples of TiN on to Silicon substrates. My team is trying to assess the thickness of TiN films by EDS-SEM spectra linked with Monte Carlo simulation in DTSA-II Microscopium software. We are interested in some setups of experimets.

I am writing a DTSA-II scrip to compute the spectra at several incident angles. In the GUI is posible do the simulation changing the incident angle in the Spectrum Simulation Window, but it is manual and very time consuming if I need several spectra. I have used the library dtsa2.mcSimulate3 as mc  with the function mc.multiFilm an with Python iterartion is posible compute the EDS spectra in only one run of a set of thickness values, but I donĀ“t know how to do the same with the incident angle value. Because, the incident angle is not an argument of  mc.multiFilm function. I have been employing DTSA scripting for not long time, maybe exist another function to simulate this.

Do you have some example of DTSA-II scripting changing the incident angle in MC EDSX spectrum simulation?

Do you recomend me another function in DTSA-II scripyting that could be useful for that purpouse?

Thanks!!

Greatings


PD:


import dtsa2.mcSimulate3 as mc

th # thickness
TiN # TiN epq material
Silicon # Pure epq silicon

mc.multiFilm([[TiN,(th)*1e-9],[Silicon,10000e-6]],d1,eo,False,1000,10)


NicholasRitchie

  • Moderator
  • Professor
  • *****
  • Posts: 89
    • NIST DTSA-II
Re: Scripting DTSA-II Monte Carlo simulation several incident angles values
« Reply #1 on: December 29, 2021, 06:22:13 AM »
Inside "mcSimulate3.py", MultiFilm is defined like this:

Quote
def buildFilm(monte, chamber, origin, buildParams):
    sr = chamber
    pos = origin
    for (mat, thickness,) in buildParams["Layers"]:
        if thickness <= 0.0:
            raise "The layer thickness must be larger than zero."
        monte.addSubRegion(sr, mat, nm.MultiPlaneShape.createFilm([0.0, 0.0, -1.0], pos, thickness))
        pos = epu.Math2.plus(pos, [0.0, 0.0, thickness + 1.0e-12])

def multiFilm(layers, det, e0=20.0, withPoisson=True, nTraj=defaultNumTraj, dose=defaultDose, sf=defaultCharFluor, bf=defaultBremFluor, xtraParams=defaultXtraParams):
    """multiFilm(layers, det, e0=20.0, withPoisson=True, nTraj=defaultNumTraj, dose=defaultDose, sf=defaultCharFluor, bf=defaultBremFluor, xtraParams={}):
    Monte Carlo simulate a spectrum from a multilayer thin film.  Layers is a iterable list of \
    [material,thickness]. Note the materials must have associated densities."""
    tmp = u"MC simulation of a multilayer film [%s] at %0.1f keV%s%s" % (",".join("%0.0f nm of %s" % (1.0e9 * layer[1], layer[0]) for layer in layers), e0, (" + CSF" if sf else ""), (" + BSF" if bf else ""))
    return base(det, e0, withPoisson, nTraj, dose, sf, bf, tmp, buildFilm, {"Layers": layers }, xtraParams)
where buildFilm(...) does the hard work of creating the sample and base(...) does the work of running the analysis.

What you want to do is to add a new build argument to buildFilm(...).  Something like this
Quote
def buildFilm2(monte, chamber, origin, buildParams):
    sr = chamber
    pos = origin
    surfaceNormal = buildParams["Orientation"]
    for (mat, thickness,) in buildParams["Layers"]:
        if thickness <= 0.0:
            raise "The layer thickness must be larger than zero."
        monte.addSubRegion(sr, mat, nm.MultiPlaneShape.createFilm(surfaceNormal, pos, thickness))
        pos = epu.Math2.plus(pos, epu.Math2.multiply(-(thickness+1.0e-12), surfaceNormal))

def multiFilm2(layers, surfaceNormal, det, e0=20.0, withPoisson=True, nTraj=mc3.defaultNumTraj, dose=mc3.defaultDose, sf=mc3.defaultCharFluor, bf=mc3.defaultBremFluor, xtraParams=mc3.defaultXtraParams):
    """multiFilm2(layers, surfaceNormal, det, e0=20.0, withPoisson=True, nTraj=defaultNumTraj, dose=defaultDose, sf=defaultCharFluor, bf=defaultBremFluor, xtraParams={}):
    Monte Carlo simulate a spectrum from a multilayer thin film.  Layers is a iterable list of \
    [material,thickness]. Note the materials must have associated densities."""
    tmp = u"MC simulation of a multilayer film [%s] at %0.1f keV%s%s" % (",".join("%0.0f nm of %s" % (1.0e9 * layer[1], layer[0]) for layer in layers), e0, (" + CSF" if sf else ""), (" + BSF" if bf else ""))
    return mc3.base(det, e0, withPoisson, nTraj, dose, sf, bf, tmp, buildFilm2, {"Layers": layers, "Orientation" : surfaceNormal }, xtraParams)
Now, "surfaceNormal = [ 0.0, 0.0, -1.0 ]" should produce the same results as multiFilm(...).  Remember positive Z is down (increasing working distance.)   In general for tilt of phi along the axis in the x-y plane defined by theta, "surfaceNormal = [ sin(phi)*cos(th), sin(phi)*sin(th), -cos(phi) ]"

So something like this:
Quote
> import dtsa2.mcSimulate3 as mc3
> th, phi = 0.0, jl.Math.PI*5/180.0
> sn = [ jl.Math.sin(phi)*jl.Math.cos(th), jl.Math.sin(phi)*jl.Math.sin(th), -jl.Math.cos(phi) ]
> def buildFilm2(monte, chamber, origin, buildParams):
    sr = chamber
    pos = origin
    surfaceNormal = buildParams["Orientation"]
    for (mat, thickness,) in buildParams["Layers"]:
        if thickness <= 0.0:
            raise "The layer thickness must be larger than zero."
        monte.addSubRegion(sr, mat, nm.MultiPlaneShape.createFilm(surfaceNormal, pos, thickness))
        pos = epu.Math2.plus(pos, epu.Math2.multiply(-(thickness+1.0e-12), surfaceNormal))

> def multiFilm2(layers, surfaceNormal, det, e0=20.0, withPoisson=True, nTraj=mc3.defaultNumTraj, dose=mc3.defaultDose, sf=mc3.defaultCharFluor, bf=mc3.defaultBremFluor, xtraParams=mc3.defaultXtraParams):
    """multiFilm2(layers, surfaceNormal, det, e0=20.0, withPoisson=True, nTraj=defaultNumTraj, dose=defaultDose, sf=defaultCharFluor, bf=defaultBremFluor, xtraParams={}):
    Monte Carlo simulate a spectrum from a multilayer thin film.  Layers is a iterable list of \
    [material,thickness]. Note the materials must have associated densities."""
    tmp = u"MC simulation of a multilayer film [%s] at %0.1f keV%s%s" % (",".join("%0.0f nm of %s" % (1.0e9 * layer[1], layer[0]) for layer in layers), e0, (" + CSF" if sf else ""), (" + BSF" if bf else ""))
    return mc3.base(det, e0, withPoisson, nTraj, dose, sf, bf, tmp, buildFilm2, {"Layers": layers, "Orientation" : surfaceNormal }, xtraParams)

> tin = material("TiN", 5.2)
> si = material("Si",2.4)
> layers = [ [ tin, 1.0e-8 ], [si, 1.0e-3] ]
> display(mc3.multiFilm(layers, d2, 10.0))
> display(multiFilm2(layers, sn, d2, 10.0))
> xp = { 'Trajectories': 2.0e-7, 'TrajSize':1024, 'TrajCount':100 } # To generate trajectory images...
> display(multiFilm2(layers, sn, d2, 10.0, xtraParams=xp))

Use care to determine which direction to tilt your sample.  Plus my densities are bogus.
"Do what you can, with what you have, where you are"
  - Teddy Roosevelt