Hyperparameter selection¶
Running an experiment requires the user to select simulation, preprocessing, and refinement settings. These choices are recorded in experiment.yaml. diffBloch keeps this file short by supplying
defaults for common settings and automatically deriving quantities already defined by the input
data. Importantly, values specified in the experiment.yaml will override defaults.
This page lists every config, its default, and what it controls. For guidance in selecting appropriate g_max, sg_max, and rocking_curve_sampling, see Convergence testing.
Values read from .cif and .cif_pets¶
Some values are deliberately not config fields in diffBloch, they are read from the structure .cif or .cif_pets file at load time. These include:
Value |
Default |
Where it is read |
Notes |
|---|---|---|---|
Electron energy / wavelength |
None; required |
|
Specified during data reduction. |
Unit cell ( |
None; required |
|
The |
UB orientation matrix |
None; required |
|
The UB matrix and each rotation’s goniometer angles supply its starting orientation. |
Data-collection geometry |
None; required |
|
PETS values |
Integration semiangle |
None; required |
Per-rotation |
Interpreted as the continuous-rotation tilt half-width or the fixed precession-cone angle. I |
Apparent mosaicity (degrees) |
None |
|
Only used when |
sample¶
Fixed sample properties (diffBloch.config.schema.SampleConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Seed thickness candidates in Å, shared by every rotation unless |
|
|
Optional starting mean thickness in Å for each pooled dataset, keyed by its exact |
inputs:
multi_dataset: true
exp_data: [dataset_1.cif_pets, dataset_2.cif_pets]
sample:
mean_thickness_by_dataset:
dataset_1.cif_pets: 400.0
dataset_2.cif_pets: 800.0
blochwave¶
Bloch wave simulation hyperparameters (BlochwaveConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Solver used for preprocessing, inference, and refinement. Use |
|
|
Include absorption as an imaginary structure-factor contribution. |
|
|
Relative excitation-error cutoff. See |
|
|
Absolute excitation-error margin. See |
|
|
Tilt samples integrated per rocking curve. See Convergence testing. |
|
|
|
|
|
See Union coupling. |
|
|
Choose union sections adaptively. See Union coupling. |
|
|
Number of union sections when adaptive splitting is disabled. See Union coupling. |
|
|
Threshold for adaptive splitting. See Union coupling. |
|
|
Largest reflection \(g\) vector simulated (Å⁻¹). Off-diagonal structure factors extend to \(2g_\mathrm{max}\). |
|
|
Maximum excitation-error magnitude (Å⁻¹) for a beam to enter the simulation at a sampled tilt. |
|
|
Zero-based |
rsg and dsg¶
Continuous-rotation data is recorded in the .cif_pets file as overlapping virtual frames
(Klar et al., 2023). Because the frames overlap, a
given reflection can appear in several neighbouring frames, but should only be fully integrated in
one of them. rsg and dsg identify the frames in which a reflection passes sufficiently far
through the Bragg condition to be treated as fully integrated.
For each reflection, diffBloch calculates its excitation error \(|S_g|\) at the centre of the frame and the excitation-error half-range \(\Delta S_g\) swept from the centre to the edge of that frame. The reflection is retained when both conditions are satisfied:
rsg is dimensionless. It limits the reflection’s central excitation error relative to its swept
range. Increasing rsg retains reflections farther from the centre of that range. dsg is an
absolute margin in Å⁻¹. Increasing dsg rejects reflections that only just enter the integration
range. These parameters select the reflections compared with experiment; they do not select the
beams included in the Bloch wave calculation.
Union coupling¶
Each virtual frame covers a small angular range of the crystal’s rotation. To simulate its
integrated intensity, diffBloch samples a rocking curve across that range. rocking_curve_sampling
sets the number of samples. Each sample is a tilt: one crystal orientation within the virtual
frame. The intensities calculated at all tilts are summed to give the simulated integrated
intensity for that frame.
Changing the tilt changes every reflection’s excitation error \(S_g\). At each tilt, beams are
selected when \(|S_g| < sg_\mathrm{max}\). In per_tilt mode, diffBloch applies this test and
builds a separate structure matrix at every tilt. This keeps each matrix as small as possible, but
repeatedly calculates \(S_g\) for all candidate reflections and prevents the tilts from being
solved together efficiently.
union mode groups neighbouring tilts. Every tilt in a group uses one combined beam set containing
the beams selected at the group’s boundaries. The off-diagonal elements describe coupling between
these beams through the structure factors and are reused across the group. The diagonal contains
the excitation error of each beam and is recalculated for every tilt. The matrices are then solved
as a batch, making better use of GPU parallelization. This is usually faster when the individual
structure matrices are small or moderate in size.
The combined beam set is larger than the set needed by any one tilt. Union mode therefore solves
larger matrices and stores several of them on the GPU at once. When the matrices are already large,
or GPU memory is nearly full, this cost can outweigh the benefit of batching. In that case,
coupling_mode: "per_tilt" is likely to be faster and use less memory. See
Devices and scaling for measured comparisons.
With union_adaptive: true, diffBloch makes shorter groups where the beam set changes rapidly and
longer groups where it remains stable: starting from one chunk spanning the whole rocking curve,
each chunk’s midpoint tilt is checked against the beam union already covered by its two endpoints,
and the chunk is split in two and recursed into if the midpoint would add more than
union_max_new_beams_pct of new beams beyond that union. Adaptive chunking costs more beam-set
unions up front (to evaluate the split predicate) but generally solves fewer total beams than a
fixed split sized conservatively enough to avoid under-coupling.
With union_adaptive: false, fixed_n_segments sets the number of equal groups regardless of how
much the beam set actually changes across them – more predictable when a fixed, known chunk count
is wanted instead.
preprocess¶
Which preprocessing steps run and their search bounds (PreprocessConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Run the per-rotation Nelder-Mead orientation search. |
|
|
Run the per-rotation thickness grid search. |
|
|
Which optimization stage runs first when both are enabled. |
preprocess.orientation.nelder_mead¶
Bounds for the local orientation search (NelderMeadOptimizationConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Initial simplex step size, degrees. |
|
|
Maximum Nelder-Mead iterations ( |
|
|
Simplex convergence tolerance on the orientation parameters ( |
|
|
Simplex convergence tolerance on the objective ( |
|
|
Penalize trial orientations that match fewer reflections than the seed, discouraging the search from drifting to a trivially-easier beam set. |
preprocess.thickness¶
Bounds for the per-rotation thickness grid search (ThicknessOptimizationConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Lower bound, Å. |
|
|
Upper bound, Å. |
|
|
Number of evenly-spaced grid candidates. |
|
|
Write one wR2-vs-thickness PNG per rotation ( |
loss_metrics¶
The one residual driving both preprocessing search and gradient refinement (LossMetricsConfig).
Top-level, not nested under refinement, because it governs preprocessing too.
Field |
Default |
What it does |
|---|---|---|
|
|
|
refinement¶
Field |
Default |
What it does |
|---|---|---|
|
|
Number of gradient-refinement epochs. |
|
|
|
|
|
Learning rate (Adam/AdamW; L-BFGS uses its own internal line search). |
refinement.trainable¶
Field |
Default |
What it does |
|---|---|---|
|
|
|
|
|
|
|
|
|
refinement.split¶
Train/validation split (DataSplitConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
|
|
|
Fraction of rotations held out when |
refinement.thickness_nn¶
Apparent-thickness neural network used by the default refinement path (ThicknessNNConfig).
Field |
Default |
What it does |
|---|---|---|
|
|
Whether a learned thickness model replaces the fixed per-rotation seed during refinement. |
|
|
Number of thickness samples the network’s input encoding uses. |
|
|
Whether thickness itself is sampled as part of the network’s forward pass. |
|
|
Functional form of the network’s output (currently only one implemented). |
|
|
Lower bound, Å, for the network’s thickness output. |
|
|
Upper bound, Å, for the network’s thickness output. |
|
|
Random seed for the network’s initial weights. |
With inputs.multi_dataset: true, one network is trained per dataset, each scoped to its own
rotations and normalizing its own tilt range; these fields configure every network identically
(including init_seed — per-dataset seeds would make results depend on exp_data ordering).
inputs¶
Input file references, relative to the experiment directory only (Inputs).
Field |
Default |
What it does |
|---|---|---|
|
required |
Relative path to the structure |
|
required |
Relative path to a |
|
|
See Combining multiple datasets below. |
|
|
Include hydrogen atom sites from the structure CIF (molecular crystals). |
|
|
Force every atom to refine with isotropic ADPs. CIF |
Combining multiple datasets¶
inputs.multi_dataset: true pools rotations from several .cif_pets files (inputs.exp_data as a
list of 2+ paths) into one experiment. Each dataset keeps its own orientation, integration
geometry, and thickness seed. Two situations call for it:
Beam damage series — repeat measurements of the same crystal taken at increasing dose. Each dataset is its own
.cif_petsfile, but they refine one shared structure.Low-symmetry structures — a single tilt series from one crystal orientation range may not cover enough of reciprocal space to constrain the structure well when the space group has few symmetry operations to fill in the gaps. Multiple datasets from different crystal orientations/mounts fill in coverage that one series alone would leave thin.
Each dataset is preprocessed and checkpointed on its own (plan.<stem>.npz per file, with its own
integration geometry – precession angles may differ between files), and the settled per-dataset
plans are pooled in memory just before refinement. The learned thickness model follows the same
per-dataset shape: one refinement.thickness_nn network per file, reported per dataset. See
Inputs and outputs for the mechanics (per-dataset checkpoints, the
first-file authoritative cell, one-energy rule, rotation-index offsets) and
Reproducibility for what each per-dataset lock verifies.
Set a different initial mean thickness for each dataset like this:
inputs:
structure: structure.cif
multi_dataset: true
exp_data:
- dataset_1.cif_pets
- dataset_2.cif_pets
sample:
mean_thickness_by_dataset:
dataset_1.cif_pets: 400.0
dataset_2.cif_pets: 800.0
Every rotation in each dataset starts from that dataset’s assigned value.