refine: fit the direction of the goniometer axis, not its length
The residual applies angle_rad * |rot_vec| and rot_vec is a free three-vector, so the first pass has been fitting a goniometer rotation SCALE nobody asked for. GoniometerAxis::Axis() then normalises it away on write-back, and RotationIndexer scores the candidate with the normalised axis - so the cell that won the fit is judged under a rotation model the fit did not use. Measured over 43 rotation datasets: the length reaches 1.2%, and the fit-vs-score disagreement a median 0.124 deg and up to 6.19 deg of goniometer angle, against rocking widths of 0.05-0.36 deg. That score picks the lattice class, which nothing later revisits. The fitted length is not a usable measurement of anything either: on synthetic data it recovers 54% of a known scale error, repeated first passes on one dataset disagree with each other in sign, 26 of 43 datasets disagree with themselves, and on the one dataset with a proven 1.3% stage fault it comes out negative. It is absorbing other systematics. The rotation scale is measured properly, once, with cross-validation and gates, in PostRefine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
@@ -455,6 +455,18 @@ bool XtalOptimizerInternal(XtalOptimizerData &data,
|
||||
|
||||
if (!data.refine_rotation_axis) {
|
||||
problem.SetParameterBlockConstant(rot_vec);
|
||||
} else {
|
||||
// Only the DIRECTION of the goniometer axis is a parameter. The residual applies
|
||||
// angle_rad * |rot_vec|, so a free three-vector also fits a rotation SCALE - which
|
||||
// GoniometerAxis::Axis() then normalises away, leaving the candidate scored by
|
||||
// RotationIndexer::accumulate() under a rotation model the fit did not use. Measured
|
||||
// over the corpus, that length reached 1.2 % and the fit/score disagreement a whole
|
||||
// degree of goniometer angle. It is not a usable measurement either: on synthetic
|
||||
// data it recovers 54 % of a known scale error, repeated first passes on one dataset
|
||||
// disagree with each other in SIGN, and on the one dataset with a real 1.3 % stage
|
||||
// fault it comes out negative. The rotation scale is measured properly, once, with
|
||||
// four gates and a jackknife, in PostRefine.
|
||||
problem.SetManifold(rot_vec, new ceres::SphereManifold<3>);
|
||||
}
|
||||
|
||||
if (!data.refine_unit_cell) {
|
||||
|
||||
Reference in New Issue
Block a user