Bragg prediction: how far to walk the lattice is a setting, not a literal
max_hkl was hardcoded to 100 at the one place production builds the prediction settings, so the only way to change it was to edit and rebuild - and it is not a constant of the method, it is a property of the cell. An axis is truncated once a/d_min exceeds it: 100 covers a 150 A axis at 1.5 A, but the same axis at 1.0 A, or a 250 A axis anywhere, loses its outermost reflections with nothing said. Move it into BraggIntegrationSettings next to the other prediction/integration parameters and add rugnux --max-hkl (1..511, default 100 - no behaviour change). Like the integration radii and the background trim it stays out of the OpenAPI, so the broker keeps the default it has today and live analysis cannot be handed a range that would not finish; the offline front end, which knows its cell, can ask for more. RugnuxCommandLine emits it when it is not the default. Measured on five rotation crystals at --max-hkl 200: two are bit-identical at no cost, and three were being truncated - one gains 419k observations (+17%) and takes its high-shell CC1/2 from 15.1% to 25.8% for +14% wall clock, the other two gain 12k and 5.8k observations with CC1/2 76.6->82.4% and 52.1->55.3% for +9% and +1%. ISa is unchanged throughout, and no frame overflowed the prediction buffer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,3 +108,16 @@ BraggIntegrationSettings &BraggIntegrationSettings::BackgroundTrimFraction(float
|
||||
float BraggIntegrationSettings::GetBackgroundTrimFraction() const {
|
||||
return bkg_trim_fraction;
|
||||
}
|
||||
|
||||
BraggIntegrationSettings &BraggIntegrationSettings::MaxHKL(int input) {
|
||||
check_min("Maximum hkl index", input, 1);
|
||||
// The GPU predictor launches one thread per candidate, so the cost is (2n+1)^3: 511 is 1.1e9
|
||||
// candidates per frame, already far past the point where prediction dominates a run.
|
||||
check_max("Maximum hkl index", input, 511);
|
||||
max_hkl = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int BraggIntegrationSettings::GetMaxHKL() const {
|
||||
return max_hkl;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ class BraggIntegrationSettings {
|
||||
// keeps the tuned high-side sigma-clip for broadband (non-zero bandwidth) data instead. 0 = plain ring
|
||||
// mean (rugnux --background-trim).
|
||||
float bkg_trim_fraction = 0.10f;
|
||||
// Half-width of the hkl cube the predictor walks: every reflection with |h|,|k|,|l| <= this is
|
||||
// tested against the Ewald sphere, and nothing outside it can ever be predicted. An axis is
|
||||
// truncated once a/d_min exceeds this, so 100 covers a 150 A axis at 1.5 A but not at 1.0 A. The
|
||||
// cost is the cube (2n+1)^3 of candidates on the GPU, which is why it is not simply set high:
|
||||
// raising it lengthens every frame's prediction whether or not the cell needs the room.
|
||||
int max_hkl = 100;
|
||||
|
||||
public:
|
||||
BraggIntegrationSettings& R1(float input);
|
||||
@@ -36,6 +42,7 @@ public:
|
||||
BraggIntegrationSettings& FixedProfileRadius_recipA(std::optional<float> input);
|
||||
BraggIntegrationSettings& Integrator(IntegratorMode input);
|
||||
BraggIntegrationSettings& BackgroundTrimFraction(float input);
|
||||
BraggIntegrationSettings& MaxHKL(int input);
|
||||
|
||||
|
||||
[[nodiscard]] IntegratorMode GetIntegrator() const;
|
||||
@@ -47,4 +54,5 @@ public:
|
||||
|
||||
[[nodiscard]] float GetMinimumSigmaInRegardsToI() const;
|
||||
[[nodiscard]] float GetBackgroundTrimFraction() const;
|
||||
[[nodiscard]] int GetMaxHKL() const;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as
|
||||
* rugnux: Per-image **geometry refinement** weights spots by confidence.
|
||||
* rugnux: `--min-image-cc` now works for rotation data (opt-in).
|
||||
* rugnux: New `--search-min-zeta` (rotation default 0.85).
|
||||
* rugnux: New `--max-hkl` sets how far the Bragg predictor walks the lattice (default 100, unchanged). An axis is truncated once `a/d_min` exceeds it, so a long-axis or high-resolution dataset was quietly losing its outermost reflections; raising it recovers them at a cost that grows as the cube.
|
||||
* rugnux: Reports how close a symmetry axis lies to the spindle.
|
||||
* rugnux: Azimuthal-integration and spot-finding resolution limits default to the detector — including **rotation** data, which no longer keeps a 1.5 Å spot-finding limit (`--spot-high-resolution <num>` still sets one).
|
||||
* **API (breaking for older clients)**: the `image_scale_b` value is gone from the `plot_type` enum, so a client that requests that plot now gets an error rather than a curve. Also, `azim_int_settings.high_q_recipA` and `spot_finding_settings.high_resolution_limit` are no longer `required`. Both now mean "as far as the detector reaches" when unset, and are **omitted** from the response instead of carrying a placeholder value. A client generated from an rc.160-or-earlier spec that assumes the field is always present (the generated C++ `from_json` does `j.at(...)`) raises when it is missing. Because the azimuthal-integration limit now defaults to unset, a stock rc.161 broker omits `high_q_recipA` from `GET /config/azim_int` with no operator action. Regenerate the client (`jfjoch-client` 1.0.0-rc.161, `frontend/src/client`) or read both fields as optional.
|
||||
|
||||
@@ -268,6 +268,7 @@ Integration:
|
||||
| `--integrator <txt>` | Spot integrator: `gaussian` (profile-fit, default) \| `empirical` \| `boxsum` (classical fallback) |
|
||||
| `--integration-radius <r>` | Signal-box radius `r1`, or `r1,r2,r3` (px). One value ⇒ `r2=r1+2`, `r3=r1+4` |
|
||||
| `--background-trim <f>` | Monochromatic (rotation + still): symmetric trimmed-mean fraction for the background ring, 0≤f<0.5 (default 0.10; 0 = plain mean) — removes the high-side bias that over-subtracts weak high-angle spots |
|
||||
| `--max-hkl <n>` | Predict reflections with \|h\|,\|k\|,\|l\| ≤ `n` (default 100, max 511). An axis is truncated once `a/d_min` exceeds `n`, so a long axis — or a short one taken to high resolution — silently loses its outermost reflections. Cost grows as `(2n+1)³` candidates per frame, so raise it only where the cell needs the room |
|
||||
| `--bandwidth <num>` | Relative X-ray bandwidth FWHM (e.g. `0.01` for a 1% DMM); default from file or 0 (monochromatic) |
|
||||
|
||||
Geometry overrides (defaults are taken from the input file; override them to reprocess with a corrected geometry):
|
||||
|
||||
@@ -438,7 +438,7 @@ void IndexAndRefine::QuickPredictAndIntegrate(DataMessage &msg,
|
||||
const BraggPredictionSettings settings_prediction{
|
||||
.high_res_A = experiment.GetBraggIntegrationSettings().GetDMinLimit_A(),
|
||||
.ewald_dist_cutoff = ewald_dist_cutoff,
|
||||
.max_hkl = 100,
|
||||
.max_hkl = experiment.GetBraggIntegrationSettings().GetMaxHKL(),
|
||||
// Centering is a hypothesis to confirm, not assume: with no user-fixed space group, predict
|
||||
// in P so the centering-absent reflections are integrated and the space-group search can
|
||||
// confirm or disprove centering (and catch a missed superstructure). A user-fixed space
|
||||
|
||||
@@ -124,6 +124,8 @@ std::string RugnuxCommandLine(const ProcessConfig &config,
|
||||
// custom fraction, or 0 when the box is unchecked) to reproduce the GUI's choice faithfully.
|
||||
if (bragg.GetBackgroundTrimFraction() != 0.10f)
|
||||
add("--background-trim", num(bragg.GetBackgroundTrimFraction()));
|
||||
if (bragg.GetMaxHKL() != 100)
|
||||
add("--max-hkl", std::to_string(bragg.GetMaxHKL()));
|
||||
|
||||
if (config.rotation_indexing) {
|
||||
if (config.two_pass_rotation)
|
||||
|
||||
@@ -117,6 +117,7 @@ void print_usage() {
|
||||
std::cout << " Integration" << std::endl;
|
||||
std::cout << " --bandwidth <num> Relative X-ray bandwidth FWHM (e.g. 0.01 for 1% DMM); default from file or 0" << std::endl;
|
||||
std::cout << " --integration-radius <r> Signal-box radius r1, or r1,r2,r3 (px). One value => r2=r1+2, r3=r1+4" << std::endl;
|
||||
std::cout << " --max-hkl <n> Predict reflections with |h|,|k|,|l| <= n (default 100). Raise for a long axis: an axis is truncated once a/d_min exceeds n. Cost grows as (2n+1)^3" << std::endl;
|
||||
std::cout << " --background-trim <f> Monochromatic (rotation + still): symmetric trimmed-mean fraction for the background ring (0<=f<0.5, default 0.10; 0 = plain mean). Removes the high-side bias that over-subtracts weak high-angle spots (broadband data keep the sigma-clip instead)" << std::endl;
|
||||
std::cout << " --integrator <txt> Spot integrator boxsum|gaussian|empirical (default: gaussian profile-fit; boxsum is the classical fallback)" << std::endl;
|
||||
std::cout << " --simple-stills stills: treat every reflection as a full (p=1, single-pass scale/merge); disables the default physical partiality post-refinement" << std::endl;
|
||||
@@ -164,6 +165,7 @@ enum {
|
||||
OPT_BANDWIDTH,
|
||||
OPT_INTEGRATION_RADIUS,
|
||||
OPT_BACKGROUND_TRIM,
|
||||
OPT_MAX_HKL,
|
||||
OPT_REJECT_OUTLIERS,
|
||||
OPT_REFERENCE_COLUMN,
|
||||
OPT_MODEL,
|
||||
@@ -271,6 +273,7 @@ static option long_options[] = {
|
||||
{"bandwidth", required_argument, nullptr, OPT_BANDWIDTH},
|
||||
{"integration-radius", required_argument, nullptr, OPT_INTEGRATION_RADIUS},
|
||||
{"background-trim", required_argument, nullptr, OPT_BACKGROUND_TRIM},
|
||||
{"max-hkl", required_argument, nullptr, OPT_MAX_HKL},
|
||||
{"integrator", required_argument, nullptr, OPT_INTEGRATOR},
|
||||
{"simple-stills", no_argument, nullptr, OPT_SIMPLE_STILLS},
|
||||
{"detect-ice-rings", optional_argument, nullptr, OPT_DETECT_ICE_RINGS},
|
||||
@@ -550,6 +553,7 @@ static int RunRugnux(int argc, char **argv) {
|
||||
std::optional<int> report_shell_count; // --resolution-shells
|
||||
std::optional<std::string> integration_radius_arg; // "r1" or "r1,r2,r3"
|
||||
std::optional<double> background_trim_arg; // --background-trim: background-ring trimmed-mean fraction
|
||||
std::optional<int64_t> max_hkl_arg; // --max-hkl: half-width of the predicted hkl cube
|
||||
std::optional<IntegratorMode> integrator_mode; // --integrator boxsum|gaussian|empirical
|
||||
bool simple_stills_flag = false; // --simple-stills: disable the default stills partiality post-refinement
|
||||
std::optional<double> outlier_reject_nsigma; // merge per-observation outlier rejection
|
||||
@@ -841,6 +845,9 @@ static int RunRugnux(int argc, char **argv) {
|
||||
case OPT_BACKGROUND_TRIM:
|
||||
background_trim_arg = parse_double_arg(optarg, "--background-trim", logger);
|
||||
break;
|
||||
case OPT_MAX_HKL:
|
||||
max_hkl_arg = parse_number_arg<int64_t>(optarg, "--max-hkl", logger, 1, 511);
|
||||
break;
|
||||
case OPT_INTEGRATOR:
|
||||
if (strcmp(optarg, "boxsum") == 0) integrator_mode = IntegratorMode::BoxSum;
|
||||
else if (strcmp(optarg, "gaussian") == 0) integrator_mode = IntegratorMode::ProfileGaussian;
|
||||
@@ -1490,6 +1497,13 @@ static int RunRugnux(int argc, char **argv) {
|
||||
: "profile (empirical)");
|
||||
}
|
||||
|
||||
if (max_hkl_arg) {
|
||||
BraggIntegrationSettings bis = experiment.GetBraggIntegrationSettings();
|
||||
bis.MaxHKL(static_cast<int>(*max_hkl_arg));
|
||||
experiment.ImportBraggIntegrationSettings(bis);
|
||||
logger.Info("Predicting reflections with |h|,|k|,|l| <= {}", *max_hkl_arg);
|
||||
}
|
||||
|
||||
if (background_trim_arg) {
|
||||
BraggIntegrationSettings bis = experiment.GetBraggIntegrationSettings();
|
||||
bis.BackgroundTrimFraction(static_cast<float>(*background_trim_arg));
|
||||
|
||||
Reference in New Issue
Block a user