Pr₂NiO₄ — single-crystal neutron CW — basic¶
Verifies calculated F² values for a constant-wavelength neutron single-crystal reference with anisotropic ADPs.
Refinement: the overall scale only; all other parameters are taken from the FullProf reference.
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='pr2nio4')
structure.space_group.name_h_m = 'F m m m' # FullProf Space group symbol
structure.cell.length_a = 5.417799 # FullProf a
structure.cell.length_b = 5.414600 # FullProf b
structure.cell.length_c = 12.483399 # FullProf c
# Anisotropic sites carry the FullProf β tensor directly: ``adp_type`` is
# set to ``'beta'`` and the dimensionless β components are assigned
# verbatim. F m m m is orthorhombic, so β11, β22, β33 are independent —
# each is set explicitly rather than left to a symmetry constraint.
# FullProf occupancy folds in the site multiplicity; the chemical
# occupancy here is the FullProf Occ scaled by the multiplicity (1.0 for
# a full site).
structure.atom_sites.create(
id='Pr', # FullProf Atom
type_symbol='Pr', # FullProf Typ
fract_x=0.5, # FullProf X
fract_y=0.5, # FullProf Y
fract_z=0.35973, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Pr']
aniso.adp_11 = 0.00710 # FullProf beta11
aniso.adp_22 = 0.00710 # FullProf beta22
aniso.adp_33 = 0.00084 # FullProf beta33
structure.atom_sites.create(
id='Ni', # FullProf Atom
type_symbol='Ni', # FullProf Typ
fract_x=0, # FullProf X
fract_y=0, # FullProf Y
fract_z=0, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Ni']
aniso.adp_11 = 0.00280 # FullProf beta11
aniso.adp_22 = 0.00280 # FullProf beta22
aniso.adp_33 = 0.00151 # FullProf beta33
structure.atom_sites.create(
id='O1', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.25, # FullProf X
fract_y=0.25, # FullProf Y
fract_z=0, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['O1']
aniso.adp_11 = 0.00500 # FullProf beta11
aniso.adp_22 = 0.00500 # FullProf beta22
aniso.adp_33 = 0.00413 # FullProf beta33
aniso.adp_12 = -0.00140 # FullProf beta12
structure.atom_sites.create(
id='O2', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0, # FullProf X
fract_y=0, # FullProf Y
fract_z=0.17385, # FullProf Z
occupancy=0.722965, # FullProf Occ 1.44593 / multiplicity
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['O2']
aniso.adp_11 = 0.01716 # FullProf beta11
aniso.adp_22 = 0.01716 # FullProf beta22
aniso.adp_33 = 0.00045 # FullProf beta33
structure.atom_sites.create(
id='Oi', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.25, # FullProf X
fract_y=0.25, # FullProf Y
fract_z=0.25, # FullProf Z
occupancy=0.074655, # FullProf Occ 0.14931 / multiplicity
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Oi']
aniso.adp_11 = 0.01033 # FullProf beta11
aniso.adp_22 = 0.01176 # FullProf beta22
aniso.adp_33 = 0.00100 # FullProf beta33
# The split interstitial oxygen Od is refined with an isotropic B.
structure.atom_sites.create(
id='Od', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.07347, # FullProf X
fract_y=0.07347, # FullProf Y
fract_z=0.17349, # FullProf Z
occupancy=0.074654, # FullProf Occ 0.59723 / multiplicity
adp_type='Biso', # FullProf Biso
adp_iso=2.31435, # FullProf Biso
)
project.structures.add(structure)
structure = StructureFactory.from_scratch(name='pr2nio4')
structure.space_group.name_h_m = 'F m m m' # FullProf Space group symbol
structure.cell.length_a = 5.417799 # FullProf a
structure.cell.length_b = 5.414600 # FullProf b
structure.cell.length_c = 12.483399 # FullProf c
# Anisotropic sites carry the FullProf β tensor directly: ``adp_type`` is
# set to ``'beta'`` and the dimensionless β components are assigned
# verbatim. F m m m is orthorhombic, so β11, β22, β33 are independent —
# each is set explicitly rather than left to a symmetry constraint.
# FullProf occupancy folds in the site multiplicity; the chemical
# occupancy here is the FullProf Occ scaled by the multiplicity (1.0 for
# a full site).
structure.atom_sites.create(
id='Pr', # FullProf Atom
type_symbol='Pr', # FullProf Typ
fract_x=0.5, # FullProf X
fract_y=0.5, # FullProf Y
fract_z=0.35973, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Pr']
aniso.adp_11 = 0.00710 # FullProf beta11
aniso.adp_22 = 0.00710 # FullProf beta22
aniso.adp_33 = 0.00084 # FullProf beta33
structure.atom_sites.create(
id='Ni', # FullProf Atom
type_symbol='Ni', # FullProf Typ
fract_x=0, # FullProf X
fract_y=0, # FullProf Y
fract_z=0, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Ni']
aniso.adp_11 = 0.00280 # FullProf beta11
aniso.adp_22 = 0.00280 # FullProf beta22
aniso.adp_33 = 0.00151 # FullProf beta33
structure.atom_sites.create(
id='O1', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.25, # FullProf X
fract_y=0.25, # FullProf Y
fract_z=0, # FullProf Z
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['O1']
aniso.adp_11 = 0.00500 # FullProf beta11
aniso.adp_22 = 0.00500 # FullProf beta22
aniso.adp_33 = 0.00413 # FullProf beta33
aniso.adp_12 = -0.00140 # FullProf beta12
structure.atom_sites.create(
id='O2', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0, # FullProf X
fract_y=0, # FullProf Y
fract_z=0.17385, # FullProf Z
occupancy=0.722965, # FullProf Occ 1.44593 / multiplicity
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['O2']
aniso.adp_11 = 0.01716 # FullProf beta11
aniso.adp_22 = 0.01716 # FullProf beta22
aniso.adp_33 = 0.00045 # FullProf beta33
structure.atom_sites.create(
id='Oi', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.25, # FullProf X
fract_y=0.25, # FullProf Y
fract_z=0.25, # FullProf Z
occupancy=0.074655, # FullProf Occ 0.14931 / multiplicity
adp_type='beta', # FullProf beta tensor
)
aniso = structure.atom_site_aniso['Oi']
aniso.adp_11 = 0.01033 # FullProf beta11
aniso.adp_22 = 0.01176 # FullProf beta22
aniso.adp_33 = 0.00100 # FullProf beta33
# The split interstitial oxygen Od is refined with an isotropic B.
structure.atom_sites.create(
id='Od', # FullProf Atom
type_symbol='O', # FullProf Typ
fract_x=0.07347, # FullProf X
fract_y=0.07347, # FullProf Y
fract_z=0.17349, # FullProf Z
occupancy=0.074654, # FullProf Occ 0.59723 / multiplicity
adp_type='Biso', # FullProf Biso
adp_iso=2.31435, # FullProf Biso
)
project.structures.add(structure)
In [5]:
Copied!
structure.show_as_text()
structure.show_as_text()
Structure 🧩 'pr2nio4' as text
| Edi | |
|---|---|
| 1 | data_pr2nio4 |
| 2 | |
| 3 | _cell.length_a 5.417799 |
| 4 | _cell.length_b 5.4146 |
| 5 | _cell.length_c 12.483399 |
| 6 | _cell.angle_alpha 90. |
| 7 | _cell.angle_beta 90. |
| 8 | _cell.angle_gamma 90. |
| 9 | |
| 10 | _space_group.name_h_m "F m m m" |
| 11 | _space_group.coord_system_code abc |
| 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 | Pr Pr 0.5 0.5 0.35973 i 8 1. 0.00924491 beta |
| 28 | Ni Ni 0. 0. 0. a 4 1. 0.0067478 beta |
| 29 | O1 O 0.25 0.25 0. e 8 1. 0.01582218 beta |
| 30 | O2 O 0. 0. 0.17385 i 8 0.722965 0.01818564 beta |
| 31 | Oi O 0.25 0.25 0.25 f 8 0.074655 0.01357409 beta |
| 32 | Od O 0.07347 0.07347 0.17349 p 32 0.074654 2.31435 Biso |
| 33 | |
| 34 | loop_ |
| 35 | _atom_site_aniso.id |
| 36 | _atom_site_aniso.adp_11 |
| 37 | _atom_site_aniso.adp_22 |
| 38 | _atom_site_aniso.adp_33 |
| 39 | _atom_site_aniso.adp_12 |
| 40 | _atom_site_aniso.adp_13 |
| 41 | _atom_site_aniso.adp_23 |
| 42 | Pr 0.0071 0.0071 0.00084 0. 0. 0. |
| 43 | Ni 0.0028 0.0028 0.00151 0. 0. 0. |
| 44 | O1 0.005 0.005 0.00413 -0.0014 0. 0. |
| 45 | O2 0.01716 0.01716 0.00045 0. 0. 0. |
| 46 | Oi 0.01033 0.01176 0.001 0. 0. 0. |
Load the FullProf reference¶
In [6]:
Copied!
FULLPROF_PROJECT_DIR = 'sc-neut-cwl_pr2nio4_basic'
FULLPROF_OUT_FILE = 'prnio.out'
FULLPROF_SCALE = 0.06298 # FullProf Scale
FULLPROF_WAVELENGTH = 0.8302 # FullProf Lambda
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-cwl_pr2nio4_basic'
FULLPROF_OUT_FILE = 'prnio.out'
FULLPROF_SCALE = 0.06298 # FullProf Scale
FULLPROF_WAVELENGTH = 0.8302 # FullProf Lambda
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='pr2nio4',
sample_form='single crystal',
beam_mode='constant wavelength',
radiation_probe='neutron',
scattering_type='bragg',
)
experiment.linked_structure.structure_id = 'pr2nio4'
experiment.linked_structure.scale = FULLPROF_SCALE
experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH
verify.set_reference_reflections(experiment, f2calc)
project.experiments.add(experiment)
experiment = ExperimentFactory.from_scratch(
name='pr2nio4',
sample_form='single crystal',
beam_mode='constant wavelength',
radiation_probe='neutron',
scattering_type='bragg',
)
experiment.linked_structure.structure_id = 'pr2nio4'
experiment.linked_structure.scale = FULLPROF_SCALE
experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH
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(
'pr2nio4',
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(
'pr2nio4',
reference=reference,
candidate=candidate,
reference_label=FULLPROF_LABEL,
candidate_label=LABEL_ED_CRYSPY,
)
Calculator for experiment 'pr2nio4' 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(
'pr2nio4',
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(
'pr2nio4',
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 'pr2nio4' already set to
cryspy
Standard fitting
📋 Using experiment 🔬 'pr2nio4' for 'single' fitting
🚀 Starting fit process with 'lmfit (leastsq)'...
📈 Goodness-of-fit progress:
| iteration | time (s) | χ² | change / status | |
|---|---|---|---|---|
| 1 | 1 | 0.03 | 34301.03 | |
| 2 | 5 | 0.14 | 0.01 | 100.0% ↓ |
| 3 | 8 | 0.23 | 0.01 |
🏆 Best goodness-of-fit (reduced χ²) is 0.01 at iteration 5
✅ 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.23 |
| 4 | 🔁 Iterations | 5 |
| 5 | 📏 Goodness-of-fit (reduced χ²) | 0.01 |
| 6 | 📏 R-factor (Rf, %) | 0.06 |
| 7 | 📏 R-factor squared (Rf², %) | 0.05 |
| 8 | 📏 Weighted R-factor (wR, %) | 0.05 |
📈 Refined parameters:
| datablock | category | entry | parameter | units | start | value | s.u. | change | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | pr2nio4 | linked_structure | scale | 0.0630 | 2.0176 | 0.0001 | 3103.50 % ↑ |
• 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 'pr2nio4' already set to
cryspy
Loading plot…
| Metric | Before | After | |
|---|---|---|---|
| 1 | Profile diff (%) | 96.88 | 0.05 |
| 2 | Max deviation (%) | 96.88 | 0.08 |
| 3 | Area ratio | 0.0312 | 1.0004 |
| 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.05 | ✅ |
| 2 | Max deviation (%) | < 6 | 0.08 | ✅ | |
| 3 | Area ratio | 0.99 to 1.01 | 1.0004 | ✅ | |
| 4 | Shape correlation | > 0.999 | 1.0000 | ✅ |
Out[10]:
True