Taurine - single-crystal neutron TOF - basic¶
Verifies calculated F2 values for a no-extinction neutron single-crystal time-of-flight baseline with isotropic ADPs.
Refinement: the overall scale only; all other parameters are taken from the FullProf reference. Extinction is set to zero in the FullProf model so the comparison focuses on the structure-factor physics shared by cryspy and FullProf.
In [2]:
Copied!
import easydiffraction as edi
from easydiffraction import ExperimentFactory
from easydiffraction import StructureFactory
from easydiffraction.analysis import verification as verify
import easydiffraction as edi
from easydiffraction import ExperimentFactory
from easydiffraction import StructureFactory
from easydiffraction.analysis import verification as verify
Build the project¶
In [3]:
Copied!
project = edi.Project()
project = edi.Project()
Define the structure¶
In [4]:
Copied!
structure = StructureFactory.from_scratch(name='taurine')
structure.space_group.name_h_m = 'P 21/c' # FullProf Space group symbol
structure.cell.length_a = 5.272901 # FullProf a
structure.cell.length_b = 11.656488 # FullProf b
structure.cell.length_c = 7.838297 # FullProf c
structure.cell.angle_beta = 94.010994 # FullProf beta
# fmt: off
_ATOMS = [
('S1', 'S', 0.19448, 0.35173, 0.34730, 1.37018),
('O1', 'O', 0.31207, 0.23951, 0.35143, 2.68011),
('O2', 'O', -0.05909, 0.33563, 0.29636, 3.50515),
('O3', 'O', 0.22105, 0.41215, 0.50573, 1.83513),
('N1', 'N', 0.26340, 0.62808, 0.33048, 2.07364),
('H1', 'H', 0.12861, 0.58669, 0.41769, 3.31527),
('H2', 'H', 0.18953, 0.71385, 0.31124, 4.46988),
('H3', 'H', 0.43970, 0.62023, 0.34592, 4.34151),
('C1', 'C', 0.34384, 0.44116, 0.20155, 1.73667),
('H11', 'H', 0.55246, 0.43345, 0.24304, 3.32279),
('H12', 'H', 0.32537, 0.38970, 0.08264, 3.05746),
('C2', 'C', 0.20029, 0.55716, 0.18272, 1.66017),
('H21', 'H', 0.27650, 0.60004, 0.07688, 2.15403),
('H22', 'H', -0.00383, 0.54767, 0.15762, 4.71303),
]
# fmt: on
for _id, _type, _x, _y, _z, _biso in _ATOMS:
structure.atom_sites.create(
id=_id, # FullProf Atom
type_symbol=_type, # FullProf Typ
fract_x=_x, # FullProf X
fract_y=_y, # FullProf Y
fract_z=_z, # FullProf Z
adp_type='Biso', # FullProf Biso
adp_iso=_biso, # FullProf Biso
)
project.structures.add(structure)
structure = StructureFactory.from_scratch(name='taurine')
structure.space_group.name_h_m = 'P 21/c' # FullProf Space group symbol
structure.cell.length_a = 5.272901 # FullProf a
structure.cell.length_b = 11.656488 # FullProf b
structure.cell.length_c = 7.838297 # FullProf c
structure.cell.angle_beta = 94.010994 # FullProf beta
# fmt: off
_ATOMS = [
('S1', 'S', 0.19448, 0.35173, 0.34730, 1.37018),
('O1', 'O', 0.31207, 0.23951, 0.35143, 2.68011),
('O2', 'O', -0.05909, 0.33563, 0.29636, 3.50515),
('O3', 'O', 0.22105, 0.41215, 0.50573, 1.83513),
('N1', 'N', 0.26340, 0.62808, 0.33048, 2.07364),
('H1', 'H', 0.12861, 0.58669, 0.41769, 3.31527),
('H2', 'H', 0.18953, 0.71385, 0.31124, 4.46988),
('H3', 'H', 0.43970, 0.62023, 0.34592, 4.34151),
('C1', 'C', 0.34384, 0.44116, 0.20155, 1.73667),
('H11', 'H', 0.55246, 0.43345, 0.24304, 3.32279),
('H12', 'H', 0.32537, 0.38970, 0.08264, 3.05746),
('C2', 'C', 0.20029, 0.55716, 0.18272, 1.66017),
('H21', 'H', 0.27650, 0.60004, 0.07688, 2.15403),
('H22', 'H', -0.00383, 0.54767, 0.15762, 4.71303),
]
# fmt: on
for _id, _type, _x, _y, _z, _biso in _ATOMS:
structure.atom_sites.create(
id=_id, # FullProf Atom
type_symbol=_type, # FullProf Typ
fract_x=_x, # FullProf X
fract_y=_y, # FullProf Y
fract_z=_z, # FullProf Z
adp_type='Biso', # FullProf Biso
adp_iso=_biso, # FullProf Biso
)
project.structures.add(structure)
In [5]:
Copied!
structure.show_as_text()
structure.show_as_text()
Structure 🧩 'taurine' as text
| Edi | |
|---|---|
| 1 | data_taurine |
| 2 | |
| 3 | _cell.length_a 5.272901 |
| 4 | _cell.length_b 11.656488 |
| 5 | _cell.length_c 7.838297 |
| 6 | _cell.angle_alpha 90. |
| 7 | _cell.angle_beta 94.010994 |
| 8 | _cell.angle_gamma 90. |
| 9 | |
| 10 | _space_group.name_h_m "P 21/c" |
| 11 | _space_group.coord_system_code b1 |
| 12 | |
| 13 | _geom.min_bond_distance_cutoff 0. |
| 14 | _geom.bond_distance_inc 0.25 |
| 15 | |
| 16 | loop_ |
| 17 | _atom_site.id |
| 18 | _atom_site.type_symbol |
| 19 | _atom_site.fract_x |
| 20 | _atom_site.fract_y |
| 21 | _atom_site.fract_z |
| 22 | _atom_site.wyckoff_letter |
| 23 | _atom_site.multiplicity |
| 24 | _atom_site.occupancy |
| 25 | _atom_site.adp_iso |
| 26 | _atom_site.adp_type |
| 27 | S1 S 0.19448 0.35173 0.3473 e 4 1. 1.37018 Biso |
| 28 | O1 O 0.31207 0.23951 0.35143 e 4 1. 2.68011 Biso |
| 29 | O2 O -0.05909 0.33563 0.29636 e 4 1. 3.50515 Biso |
| 30 | O3 O 0.22105 0.41215 0.50573 e 4 1. 1.83513 Biso |
| 31 | N1 N 0.2634 0.62808 0.33048 e 4 1. 2.07364 Biso |
| 32 | H1 H 0.12861 0.58669 0.41769 e 4 1. 3.31527 Biso |
| 33 | H2 H 0.18953 0.71385 0.31124 e 4 1. 4.46988 Biso |
| 34 | H3 H 0.4397 0.62023 0.34592 e 4 1. 4.34151 Biso |
| 35 | C1 C 0.34384 0.44116 0.20155 e 4 1. 1.73667 Biso |
| 36 | H11 H 0.55246 0.43345 0.24304 e 4 1. 3.32279 Biso |
| 37 | H12 H 0.32537 0.3897 0.08264 e 4 1. 3.05746 Biso |
| 38 | C2 C 0.20029 0.55716 0.18272 e 4 1. 1.66017 Biso |
| 39 | H21 H 0.2765 0.60004 0.07688 e 4 1. 2.15403 Biso |
| 40 | H22 H -0.00383 0.54767 0.15762 e 4 1. 4.71303 Biso |
Load the FullProf reference¶
In [6]:
Copied!
FULLPROF_PROJECT_DIR = 'sc-neut-tof_taurine_basic'
FULLPROF_OUT_FILE = 'taurine.out'
FULLPROF_SCALE = 2.711 # FullProf Scale
f2calc = verify.load_fullprof_sc_f2calc(FULLPROF_PROJECT_DIR, FULLPROF_OUT_FILE)
FULLPROF_LABEL = verify.fullprof_label(FULLPROF_PROJECT_DIR, FULLPROF_OUT_FILE)
FULLPROF_PROJECT_DIR = 'sc-neut-tof_taurine_basic'
FULLPROF_OUT_FILE = 'taurine.out'
FULLPROF_SCALE = 2.711 # FullProf Scale
f2calc = verify.load_fullprof_sc_f2calc(FULLPROF_PROJECT_DIR, FULLPROF_OUT_FILE)
FULLPROF_LABEL = verify.fullprof_label(FULLPROF_PROJECT_DIR, FULLPROF_OUT_FILE)
Create the experiment¶
In [7]:
Copied!
experiment = ExperimentFactory.from_scratch(
name='taurine',
sample_form='single crystal',
beam_mode='time-of-flight',
radiation_probe='neutron',
scattering_type='bragg',
)
experiment.linked_structure.structure_id = 'taurine'
experiment.linked_structure.scale = FULLPROF_SCALE
verify.set_reference_reflections(experiment, f2calc)
project.experiments.add(experiment)
experiment = ExperimentFactory.from_scratch(
name='taurine',
sample_form='single crystal',
beam_mode='time-of-flight',
radiation_probe='neutron',
scattering_type='bragg',
)
experiment.linked_structure.structure_id = 'taurine'
experiment.linked_structure.scale = FULLPROF_SCALE
verify.set_reference_reflections(experiment, f2calc)
project.experiments.add(experiment)
edi-cryspy VS FullProf¶
In [8]:
Copied!
calc_ed_cryspy = verify.calculate_reflections(project, experiment, 'cryspy')
LABEL_ED_CRYSPY = verify.engine_label('cryspy')
reference, candidate = verify.align_reflections(f2calc, calc_ed_cryspy)
project.display.reflection_comparison(
'taurine',
reference=reference,
candidate=candidate,
reference_label=FULLPROF_LABEL,
candidate_label=LABEL_ED_CRYSPY,
)
calc_ed_cryspy = verify.calculate_reflections(project, experiment, 'cryspy')
LABEL_ED_CRYSPY = verify.engine_label('cryspy')
reference, candidate = verify.align_reflections(f2calc, calc_ed_cryspy)
project.display.reflection_comparison(
'taurine',
reference=reference,
candidate=candidate,
reference_label=FULLPROF_LABEL,
candidate_label=LABEL_ED_CRYSPY,
)
Calculator for experiment 'taurine' already set to
cryspy
Loading plot…
Fit edi-cryspy to FullProf¶
In [9]:
Copied!
experiment.calculator.type = 'cryspy'
experiment.linked_structure.scale.free = True
project.analysis.fit()
project.display.fit.results()
calc_ed_cryspy_refined = verify.calculate_reflections(project, experiment, 'cryspy')
LABEL_ED_CRYSPY_REFINED = verify.engine_label('cryspy', note='scale only')
reference_refined, candidate_refined = verify.align_reflections(f2calc, calc_ed_cryspy_refined)
project.display.reflection_comparison(
'taurine',
reference=reference_refined,
candidate=candidate_refined,
reference_label=FULLPROF_LABEL,
candidate_label=LABEL_ED_CRYSPY_REFINED,
)
verify.report_refinement_closeness(
reference,
candidate,
candidate_refined,
)
experiment.calculator.type = 'cryspy'
experiment.linked_structure.scale.free = True
project.analysis.fit()
project.display.fit.results()
calc_ed_cryspy_refined = verify.calculate_reflections(project, experiment, 'cryspy')
LABEL_ED_CRYSPY_REFINED = verify.engine_label('cryspy', note='scale only')
reference_refined, candidate_refined = verify.align_reflections(f2calc, calc_ed_cryspy_refined)
project.display.reflection_comparison(
'taurine',
reference=reference_refined,
candidate=candidate_refined,
reference_label=FULLPROF_LABEL,
candidate_label=LABEL_ED_CRYSPY_REFINED,
)
verify.report_refinement_closeness(
reference,
candidate,
candidate_refined,
)
Calculator for experiment 'taurine' already set to
cryspy
Standard fitting
📋 Using experiment 🔬 'taurine' for 'single' fitting
🚀 Starting fit process with 'lmfit (leastsq)'...
📈 Goodness-of-fit progress:
| iteration | time (s) | χ² | change / status | |
|---|---|---|---|---|
| 1 | 1 | 0.01 | 764.53 | |
| 2 | 5 | 0.04 | 0.00 | 100.0% ↓ |
| 3 | 8 | 0.08 | 0.00 |
🏆 Best goodness-of-fit (reduced χ²) is 0.00 at iteration 7
✅ Fitting complete.
⚙️ Settings used:
| Name | Value | Description | |
|---|---|---|---|
| 1 | max_iterations | 1000 | Maximum solver iterations. |
📋 Least-squares fit results:
| Metric | Value | |
|---|---|---|
| 1 | 🧪 Minimizer | lmfit (leastsq) |
| 2 | ✅ Overall status | success |
| 3 | ⏱️ Fitting time (seconds) | 0.08 |
| 4 | 🔁 Iterations | 5 |
| 5 | 📏 Goodness-of-fit (reduced χ²) | 0.00 |
| 6 | 📏 R-factor (Rf, %) | 0.00 |
| 7 | 📏 R-factor squared (Rf², %) | 0.00 |
| 8 | 📏 Weighted R-factor (wR, %) | 0.00 |
📈 Refined parameters:
| datablock | category | entry | parameter | units | start | value | s.u. | change | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | taurine | linked_structure | scale | 2.7110 | 1.3555 | 0.0000 | 50.00 % ↓ |
• start = parameter value before refinement
• value = refined value from least-squares minimization
• s.u. = standard uncertainty (one sigma), from the covariance matrix
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
• value = refined value from least-squares minimization
• s.u. = standard uncertainty (one sigma), from the covariance matrix
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
Calculator for experiment 'taurine' already set to
cryspy
Loading plot…
| Metric | Before | After | |
|---|---|---|---|
| 1 | Profile diff (%) | 100.00 | 0.00 |
| 2 | Max deviation (%) | 100.00 | 0.00 |
| 3 | Area ratio | 2.0000 | 1.0000 |
| 4 | Shape correlation | 1.0000 | 1.0000 |
Agreement check¶
In [10]:
Copied!
verify.assert_patterns_agree([
(f'{LABEL_ED_CRYSPY_REFINED} vs {FULLPROF_LABEL}', reference_refined, candidate_refined),
])
verify.assert_patterns_agree([
(f'{LABEL_ED_CRYSPY_REFINED} vs {FULLPROF_LABEL}', reference_refined, candidate_refined),
])
| Comparison | Metric | Expected | Actual | OK | |
|---|---|---|---|---|---|
| 1 | edi 0.19.0 (cryspy 0.12.1, scale only) vs FullProf 8.40 | Profile diff (%) | < 2.5 | 0.00 | ✅ |
| 2 | Max deviation (%) | < 6 | 0.00 | ✅ | |
| 3 | Area ratio | 0.99 to 1.01 | 1.0000 | ✅ | |
| 4 | Shape correlation | > 0.999 | 1.0000 | ✅ |
Out[10]:
True