Open Didou09 opened 2 years ago
Motivation:
The compute_rockingcurve()
routine available at tofu/spectro/_rockingcurve.py
makes it possible to compute the diffraction pattern of specific crystals (material, Miller indices, etc).
In order to symplify the code, all the main characteristics of the crystals implemented have been relocated into tofu/spectro/_rockingcurve_def.py
, creating specific dictionnaries of crystals.
Example:
See below an example for the ArXVII crystal:
All the geometrical and atomic parameters useful for the computation are stored into sub-dictionnaries such as:
All values which cannot be directly written into the dictionnary (see None
values in front of each sub-dictionnary) are computed in the following parts of the file:
The positions of atoms in the elementary unit volume to fill the sub-dict ['mesh positions']['atoms']:
The elementary box volume and the associated inter-reticular spacing, filling the sub-dict ['Volume'] & ['Inter-reticular spacing']:
The whole crystal structure factor computation depending on the atoms componing it:
According to the last point, only the phases are stored inside the dictionnary. The formulas of the linear absorption coefficients and the components of the atomic scattering factor are called directly from the compute_rockingcurve()
routine because of the dependency on the wavelength.
The use of the _compute_rockingcurve()
routine works interactively with the tofu/spectro/_rockingcurve_def.py
file.
The uploading of a CrystalClass is still needed.
The line code launching the routine is showed below:
The arguments showed here are sufficient:
crystal
(here 102-Quartz
but also available 110-Quartz
)lamb
(already in Angstroms)use_non_parallelism
and its range in radians and also the temperature changes causing thermal expansion of the reticular planes therm_exp
and its range in Celsius degrees.For more information about the entry arguements, everything is explained at the beginning of the tofu/spectro/_rockingcurve.py routine:
Quartz crystal, (1, 1, 0), supposed ideal:
Quartz crystal, (1, 0, 2), supposed ideal:
Well done @adriendaros !
If I may, I would suggest the following minor changes in the dictionnary:
'mesh' : {
'type': 'hexagonal',
'positions': {
'Si': {
'u': np.r_[0.465],
'x': None,
'y': None,
'z': None,
'N': None,
},
'O': {
'u': np.r_[0.415, 0.272, 0.120],
'x': None,
'y': None,
'z': None,
'N': None,
},
},
'source': 'R.W.G. Wyckoff, Crystal Structures (1963)',
}
and
'Inter-atomic': {
'distances': {
'a0': 4.91304,
'c0': 5.40463,
},
'units': 'A',
'comments': 'within the unit cell',
' Tref': { # 'refetence temperature', but shorter
'data': 25.,
'units': 'C',
},
and
'Thermal expansion': {
'coefs': {
'alpha_a': 1.337e-5,
'alpha_c': 7.97e-6,
},
'units': '1/C',
'comments': 'in parallel directions to a0 and c0',
'source': 'R.W.G. Wyckoff, Crystal Structures',
}
and
'atomic scattering': {
'factor': {
'Si': np.r_[
12., 11., 9.5, 8.8, 8.3, 7.7, 7.27, 6.25, 5.3,
4.45, 3.75, 3.15, 2.7, 2.35, 2.07, 1.87, 1.71, 1.6,
],
'O': np.r_[
9., 7.836, 5.756, 4.068, 2.968, 2.313, 1.934, 1.710, 1.566,
1.462, 1.373, 1.294,
],
},
'source': 'Intern. Tables for X-Ray Crystallography, Vol.I,II,III,IV (1985)',
}
=> it will allow to re-use some key words like 'source', 'units', ... which makes them more generic
@cjperks7 , once @adriendaros will have implemented those last minor changes, you're good to go
@adriendaros, @cjperks7 , just to let you know:
_DCRYST
dict in _rockingcurve_def.py
to:
str.isidentifier()
: no special characters except _
, in particular no -
or /,
(or
spaces` in the names + no upper case for first letter + no starting with a number). This makes the library more robust in case we need to use these keys as input arguments of functions later.atoms
, atomic number
and number of atoms
become atoms
, atoms_Z
and atoms_nb
I am also adding a basic unit test to make sure the computation always works. It just runs it for akk known crystals at the same wavelength. It simply makes sure it is not broken and there is no bug that would stop the computation, does not check the value of the result and does not check plots.
@adriendaros , I have a question:
The key sin(theta)/lambda
(now called sin_theta_lambda
, see above for reasons), seems to never be used in the routine compute_rockingcurve()
.
=> why is it never used ?
=> is it useful in the computation ?
=> if it is not useful, should we remove it ?
@adriendaros , I have a question:
The key
sin(theta)/lambda
(now calledsin_theta_lambda
, see above for reasons), seems to never be used in the routinecompute_rockingcurve()
. => why is it never used ? => is it useful in the computation ? => if it is not useful, should we remove it ?
@Didou09 don't do that you fool, it's very useful to get the values of the atomic scattering factor, real and imaginary parts, of each kind of atoms within the crystal.
Into tofu/spectro/_rocking_def.py
, there is data corresponding to the variation of the atomic scattering
[factors
] with respect to this variable sin_theta_lambda
for each atom.
In order to interpolate the value of the scattering factor in our crystals, the routine CrystBragg_comp_lattice_spacing()
called by compute_rockingcurve()
is computing the appropriated value of sin_theta_lambda
for the crystal used, depending on the Miller indices and the wavelength targetted.
Then, you'll find into the compute_rockingcurve()
routine line 214 part Calculation of the structure factor
that this constant is called in the computation of each real and imaginary scattering factor for each atom.
So no, we can't do without it because of its great importance in the determination of the crystal structure factor.
OK, good reasons, keeping it (I did not delete it, was waiting for your answer)
Changes implemented --- (i) One can now calculate the rocking curve for Quartz with flexible choice on Miller indice by either:
crystal
name as appropriate for WEST within ['Quartz_110', 'Quartz_102']
# Computes rocking curve
dout1 = rc.compute_rockingcurve(
crystal = 'Quartz_102',
lamb = 3.96,
plot_power_ratio = False
)
# C-Mod's crystal
dcry2 = {
'material': 'Quartz',
'name': 'HIREX',
'symbol': 'Qz102',
'miller': np.r_[1., 0., 2.,],
'target': {
'ion': 'Ar16+',
'lamb': 3.96, # e-10
'units': 'm',
},
'd_hkl': 2.281124272650091,
}
dout2 = rc.compute_rockingcurve( crystal = dcry2['name'], din=dcry2, lamb = 3.96, plot_power_ratio = False )
- Note that the two methods shown above give the same rocking curve since the target wavelength in the pre-defined crystals get over-written
- Benchmarking against the x0h code for two crystals of potential interest as been conducted to good agreement between the two models
- <img width="1264" alt="Screenshot 2023-06-20 at 1 44 47 PM" src="https://github.com/ToFuProject/tofu/assets/71457911/48832bf0-4b17-4203-8c77-b350ac914fb7">
(ii) It is now simple to loop over instances of `tofu.spectro._rockingcurve.compute_rockingcurve()` for Quartz with a target wavelength and arbitrary Miller indices to scope Bragg angle v. integrated reflectivity. All one needs to do is alter the array in `dcry2['miller']` as given above
- <img width="601" alt="Screenshot 2023-06-20 at 1 49 25 PM" src="https://github.com/ToFuProject/tofu/assets/71457911/24befce2-7c18-4882-96c9-8a54bbc02bc8">
- Note that this looping will be inserted into the tofu_sparc workflow under Issue015
(iii) These changes have been propagated up into `tofu.data._class5_check()` so that one can initiate a crystal using `coll.add_crystal()` choosing the `dmat` input to either be a string or dictionary as discussed in point (i). For example, building CMOD's Johann He-like Ar spectrometer:
dcry ={
'dgeom': {
'cent': np.array([1,1,1]), # [m], center in global axis
'nin': np.array([1,0,0]), # inward normal vec
'e0': np.array([0,1,0]), # horizontal vec
'e1': np.array([0,0,1]), # vertical vec
'curve_r': np.array([1385e-3, 1385e-3]), # [m], crystal radius of curvature
'extenthalf': np.array([64e-3/2, 27e-3/2]), # [m], disp. of corners wrt cent in (hor. ver.)
},
# Defines crystal material
'dmat': {
'material': 'Quartz',
'name': 'HIREX',
'symbol': 'Qz102',
'miller': np.r_[1., 0., 2.],
'target':{
'ion': 'Ar16+',
'lamb': 3.96, # [1e-10 m], photon wavelength
'units': 'm',
},
'd_hkl': 2.281124272650091, # [m], crystal lattice spacing
},
}
coll.add_crystal( key='cry', dgeom=dcry['dgeom'], dmat=dcry['dmat'],
)
- It has been benchmarked that the expected rocking curve is stored in the `Collection` object
- <img width="591" alt="Screenshot 2023-06-20 at 1 56 09 PM" src="https://github.com/ToFuProject/tofu/assets/71457911/070e144a-3a5b-40f4-91b0-3afbb71b5ba1">
@Didou09 @adriendaros Germanium rocking curves work now 🎉
Will do a PR in ToFu and tofu_sparc tomorrow
Motivations:
Proposition:
Getting started:
Issue654_Germanium
, fromdevel
, that will be dedicated to this particular tasktofu/spectro/_rockingcurve.py
compute_rockingcurve()
Proposed todo list:
For @adriendaros (preparations):
dcryst
, it should be a dictionnary containing at least:name
: the name of the crystal material (ex:Quartz
,Germanium
)symbol
: a short version of the name of the of the crystal material (ex:Quartz
,Ge
)tofu/spectro/_rockingcurve_def.py
as a global variable:'Ge': {
'name': 'Germanium',
'symbol': 'Ge',
},
}
For @cjperks7: