How to add a stage to a refinement plan#
Take a preset from RefinementPlan, add a Stage to its stages list, and pass the plan to Refinement.fit.
A plan is an ordinary Python object, and RefinementPlan.stages is a plain list.
Each stage frees the parameters it names and keeps every parameter earlier stages freed, so the last stage refines everything together.
Add a stage at the end#
mccusker_default frees the scale, background, zero shift, lattice parameters and peak widths, and never the atoms.
This fit appends a stage that frees every atom’s Biso, on the GSAS-II tutorial fluorapatite pattern that ships with rietx:
import rietx as rx
from rietx.examples import examples_dir
data = rx.read_pattern(examples_dir() / "FAP.XRA")
structure = rx.Structure.from_cif(examples_dir() / "fluorapatite.cif")
instrument = rx.Instrument.bragg_brentano(radiation="CuKa")
instrument.background = rx.BackgroundChebyshev.with_terms(6)
plan = rx.RefinementPlan.mccusker_default()
plan.stages.append(rx.Stage("biso", ["phases.*.atoms.*.biso"]))
ref = rx.Refinement(structure, instrument)
result = ref.fit(data, plan=plan, two_theta_limits=(15, 130))
for stage in result.stages:
print(f"{stage.name:<12} {len(stage.freed):>2} freed {stage.status}")
scale_bkg 7 freed converged
zero 1 freed converged
cell 2 freed converged
profile_w 1 freed converged
profile 4 freed converged
biso 7 freed converged
closing 22 freed converged
StageResult.freed lists the parameters each stage’s names matched.
The cell stage freed two, because the hexagonal space group leaves only a and c free.
The fit ends with the closing stage, which every plan adds after its own stages (The closing stage).
It names every parameter the other stages named, so it lists all 22.
The full script is examples/guides/plan.py.
Insert or remove a stage#
Use the list’s own methods.
This inserts a Biso stage after the cell stage of mccusker_structural and drops its roughness stage:
import rietx as rx
plan = rx.RefinementPlan.mccusker_structural()
names = [stage.name for stage in plan.stages]
plan.stages.insert(names.index("cell") + 1, rx.Stage("biso_early", ["phases.*.atoms.*.biso"]))
plan.stages = [stage for stage in plan.stages if stage.name != "roughness"]
assert [stage.name for stage in plan.stages][:4] == ["scale_bkg", "zero", "cell", "biso_early"]
assert "roughness" not in [stage.name for stage in plan.stages]
Build a plan from nothing#
Pass RefinementPlan the list of stages:
import rietx as rx
plan = rx.RefinementPlan(stages=[
rx.Stage("background", ["instrument.background.*", "phases.*.scale"]),
rx.Stage("cell", ["instrument.zero_shift", "phases.*.cell.*"]),
rx.Stage("profile", ["instrument.profile.*"]),
rx.Stage("atoms", ["phases.*.atoms.*.dof.*", "phases.*.atoms.*.biso"]),
])
assert len(plan.stages) == 4
Name the parameters a stage frees#
The second argument of Stage, turn_on, is a list of parameter names such as phases.0.cell.a.
A * in a name matches any run of characters, dots included, so phases.*.atoms.*.biso matches every atom’s Biso in every phase.
Names take no square brackets, because the matching reads [0] as a set of characters and not as an index.
Parameter map lists every name.
A stage never frees a parameter the space group fixes or ties, or one you hold with Refinement.hold, whatever its names match.
The other Stage arguments have defaults.
The ones most often set are:
max_iter, the iteration limit for the stage (100);restraint_weight_scale, a factor on every restraint’s weight for the stage (1.0), which How to restrain a bond length or an angle uses;seed, a starting value for each freed parameter that sits exactly on a lower limit of 0 or above, so the fit can move it off the limit (0.0, which seeds nothing).
help(rx.Stage) lists the rest.
Where to put the new stage#
The presets follow the order of the IUCr guidelines [McCusker et al., 1999], and a new stage fits into it:
scale and background;
peak positions: the lattice parameters and the zero shift, with specimen displacement where the geometry has one;
peak widths, then asymmetry;
atom positions, then displacement parameters;
intensity corrections such as preferred orientation, extinction and surface roughness.
Free the constant width term w before u, v, x and y, or the angle-dependent terms absorb a constant offset and then fight w for it.
Free an intensity correction late, or it takes intensity that belongs to the structure.
Use a preset by name#
fit also takes a preset’s name, as plan="lab_calibrate".
PLAN_INFO maps each name to its title, description, fit modes and when to use it, so a program can offer the choice without its own list:
import rietx as rx
info = rx.PLAN_INFO["lab_calibrate"]
assert info.title == "Lab: calibrate on a standard"
assert set(rx.PLAN_INFO) >= {"mccusker_default", "mccusker_structural", "profile_only"}
Plan presets draws every stage of every preset.
See also#
About a fit follows one stage from freeing its parameters to committing its history node.
How to keep a parameter fixed whatever the plan frees stops a stage freeing one parameter.