import numpy as np
import escape as esc
esc.require("0.9.8")
The local `materials.yaml` file will be used to compile the database Loading material database from C:\dev\escape-core\notebooks\repository\scattering\materials.db
Overview of the Material Module¶
ESCAPE uses material objects to connect chemical composition, density, and scattering-length-density (SLD) values. Older versions of the library exposed only low-level constructors, which meant that users often had to supply SLD-related values by hand. That works, but it is tedious when the numbers can be derived from a formula and a density.
The material database module escape.scattering.mdb simplifies this workflow by storing common materials together with their default composition and density information.
The higher-level constructors in escape.scattering.material can then use the database directly.
This notebook shows the most common ways to define amorphous, crystalline, magnetic, and gradient materials.
Amorphous materials¶
There are several ways to define an amorphous material, depending on which quantity you want to fit. A good starting point is to ask whether the model should use mass density directly or whether it should use scattering length density (SLD).
The four common cases are:
- Mass density is a fit parameter, and the material exists in the database.
- Mass density is a fit parameter, and the material is defined only by its chemical formula.
- SLD is a fit parameter, and the material exists in the database.
- SLD is a fit parameter, and the material is defined only by its chemical formula, while density stays fixed.
We start with the first case. Here we use deuterated polystyrene, C8D8, with a mass density of $1.05\;g/cm^3$.
This material is available in the material database under the name Polystyrene.
md = esc.par("PS density", 1.05, units="g/cm^3")
PS = esc.amorphous(name="PS", mid="Polystyrene", density=md)
print(PS)
Name: PS Parameters number: 1 Parameter Value +- Error Units Fixed PS density 1.05 +- 0 g/cm^3 0
In the example above, we have defined the density parameter, but we could also use the default density value from the record by using "mdb" as the density argument.
PS = esc.amorphous(name="Polystyrene", density="mdb")
print(PS)
Name: Polystyrene Parameters number: 1 Parameter Value +- Error Units Fixed Polystyrene density 1.05 +- 0 g/cm^3 0
In the second scenario, density can be given either as a constant or as a parameter.
The density units are always $g/cm^3$.
Using the mdb keyword creates a density parameter automatically from the database value.
This workflow does not rely on a named material record. Instead, ESCAPE uses the chemical formula and the corresponding scattering-factor tables to compute the material properties.
PS = esc.amorphous(name="Polystyrene", formula="C8H8", density=1.05)
print(PS)
Name: Polystyrene Parameters number: 0 Parameter Value +- Error Units Fixed
For the third scenario, we do not fit density directly.
Instead, we use the SLD-related parameters dwsld0re and dwsld0im.
Setting density=None tells ESCAPE to skip density as an active model parameter.
The parameters dwsld0re and dwsld0im are scale factors for the real and imaginary parts of the SLD:
- $\rho_{re} = \rho_{0,re} \cdot dwsld0re$
- $\rho_{im} = \rho_{0,im} \cdot dwsld0im$
If you set these factors to 1, the SLD is still computed from the database, but it is not varied during fitting.
PS = esc.amorphous(name="Polystyrene", density=None, dwsld0re=1, dwsld0im=1)
# prints nothing
print(PS)
Name: Polystyrene Parameters number: 0 Parameter Value +- Error Units Fixed
Or with dwsld0re and dwsld0im as parameters.
dwsld0 = esc.par("dwSLD", 1)
PS = esc.amorphous(name="Polystyrene", density=None, dwsld0re=dwsld0, dwsld0im=dwsld0)
print(PS)
Name: Polystyrene Parameters number: 1 Parameter Value +- Error Units Fixed dwSLD 1 +- 0 0
Or in auto mode.
PS = esc.amorphous(name="Polystyrene", density=None, dwsld0re="auto", dwsld0im="auto")
print(PS)
Name: Polystyrene Parameters number: 2 Parameter Value +- Error Units Fixed Polystyrene DwSLD0Re 1 +- 0 0 Polystyrene DwSLD0Im 1 +- 0 0
Scenario four is straightforward.
PS = esc.amorphous(
name="Polystyrene", formula="C8H8", density=1.05, dwsld0re=dwsld0, dwsld0im=dwsld0
)
# prints nothing
print(PS)
Name: Polystyrene Parameters number: 1 Parameter Value +- Error Units Fixed dwSLD 1 +- 0 0
Crystalline materials¶
Crystalline materials are created in almost the same way as amorphous materials. The main difference is that crystalline materials do not support arbitrary formulas here, so a matching record must exist in the material database. Because of that, only the database-based workflows from the list above are available.
Below are a few examples.
GaAs = esc.crystal(name="GaAs", density="mdb")
print(GaAs)
Name: GaAs Parameters number: 1 Parameter Value +- Error Units Fixed GaAs density 5.3176 +- 0 g/cm^3 0
dwsld0 = esc.par("dwSLD0", 1)
dwsldh = esc.par("dwSLDh", 1)
GaAs = esc.crystal(
name="GaAs",
density=None,
dwsld0re=dwsld0,
dwsld0im=dwsld0,
dwsldhre=dwsldh,
dwsldhim=dwsldh,
)
print(GaAs)
Name: GaAs Parameters number: 2 Parameter Value +- Error Units Fixed dwSLD0 1 +- 0 0 dwSLDh 1 +- 0 0
Magnetic material¶
When polarized neutrons interact with a magnetic material, the model must also include the magnetic scattering length density. This term describes the part of the scattering that depends on the magnetic moment of the sample.
You can provide the magnetic SLD either as a constant or as a fit parameter using the sldm argument.
By default, sldm is None, which means that no magnetic contribution is added.
Fe = esc.amorphous(
"Fe", density="mdb", sldm=esc.par(value=5, scale=1e-6, units="1/A^2")
)
print(Fe)
Name: Fe Parameters number: 2 Parameter Value +- Error Units Fixed Fe density 7.874 +- 0 g/cm^3 0 Parameter 5 +- 0 x1e-06 1/A^2 0
Gradient materials¶
ESCAPE also supports gradient materials, which are useful when the material properties change smoothly with depth. This is especially important for layered samples, where density or composition may vary across the film thickness.
To define a gradient material, provide two extra arguments:
numslices, the number of thin slices used to approximate the gradientzvar, avariable_objrepresenting the depth direction perpendicular to the sample surface
Any argument in amorphous() that describes a parameter should be a functor_obj, or it will be converted automatically when possible.
This allows density profiles and similar quantities to depend on depth.
# linear density profile
z = esc.var("Z")
density_0 = esc.par("density_0", 7.874)
density_1 = esc.par("density_1", 8.86)
density = density_0 + (density_1 - density_0) * z
Fe = esc.amorphous("Fe", density=density, numslices=10, zvar=z)
print(Fe)
Name: Fe Parameters number: 2 Parameter Value +- Error Units Fixed density_0 7.874 +- 0 0 density_1 8.86 +- 0 0
Layered samples given by formula¶
With the support of the Material Database module, it is now possible to define multilayer samples with formulas such as this:
Air/Fe(10)/Si(1)Fe(10)//Si
Each element of this formula is the name of the material record in the database. The '/' character is a layer separator. The value in parentheses is the thickness of the layer. The first and last elements are the foreground and background layers (environment and substrate) and do not require a thickness value.
ml = esc.multilayer(
name="Multilayer", formula="Air//Fe(100)/Si(10)/Fe(100)//Si", bydensity=False
)
ml.show(source=esc.xrays(1.54056))
Layer stacks are also supported, for example:
[Fe(100)/Si(10)]x10//Si
Air foreground was added by default if skipped.
ml = esc.multilayer(
name="Multilayer", formula="[Fe(100)/Si(10)]x10//Si(2)", bydensity=True
)
ml.show(source=esc.neutrons(wavelength=4.5))
Every element in the multilayer or layerstack formula is a material name. Since the records with the same name but with different material types (i.e. amorphous, crystal) are allowed it is necessary to specify the material type which is amorphous by default. If the material type is crystal then the formula should be written as follows:
[GaAs{cr}(100)/AlAs{cr}(50)]x10//GaAs{cr}
Use {am} for amorphous materials and {cr} for crystal materials.
ml = esc.multilayer(
name="Multilayer",
formula="[GaAs{cr}(100)/AlAs{cr}(50)]x10//GaAs{cr}",
bydensity=True,
)
ml.show(source=esc.xrays(1.54056))
Customization of roughness values is also possible.
ml = esc.multilayer(name= "Multilayer", formula="[GaAs{cr}(10.3,1.1)/AlAs{cr}(5,2)]x10//GaAs{cr}(5.2)", bydensity=True) print(ml)
If your layer or material has been defined previously you can reuse it in the formula using its variable name.
Fe_custom = esc.amorphous("Fe", density="mdb")
Si_layer = esc.layer(material="Si", thkn=esc.par(value=10), bydensity=True)
# don't forget to add the globals dictionary which contain all the variables defined above
ml = esc.multilayer(
name="Multilayer",
formula="Air//Fe_custom(10)/Si_layer//Si",
bydensity=False,
globals=globals(),
)
print(ml)
Name: Multilayer Parameters number: 7 Parameter Value +- Error Units Fixed Fe density 7.874 +- 0 g/cm^3 0 Fe_custom Thickness 10 +- 0 0 Fe_custom Roughness 0.1 +- 0 0 Si Density 2.33 +- 0 0 Parameter 10 +- 0 0 Si DWSLD0Re 1 +- 0 0 Si DWSLD0Im 1 +- 0 0