From 26d751f89cc16592a9db6dc0f80d18878a1430e7 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Tue, 3 Nov 2026 08:02:20 +0100 Subject: [PATCH] rugnux: drop the panel study log (RUGNUX_PANEL_STUDY); the held-out tally keeps only the per-module sums the placement rule uses Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4 --- docs/python_client/README.md | 1 + docs/python_client/docs/DatasetSettings.md | 1 + .../docs/DetectorModulePlacement.md | 39 +++++++++++++ rugnux/RugnuxStills.cpp | 57 +++---------------- 4 files changed, 48 insertions(+), 50 deletions(-) create mode 100644 docs/python_client/docs/DetectorModulePlacement.md diff --git a/docs/python_client/README.md b/docs/python_client/README.md index bc0d3f410..c36fbe9f5 100644 --- a/docs/python_client/README.md +++ b/docs/python_client/README.md @@ -190,6 +190,7 @@ Class | Method | HTTP request | Description - [DetectorListElement](docs/DetectorListElement.md) - [DetectorModule](docs/DetectorModule.md) - [DetectorModuleDirection](docs/DetectorModuleDirection.md) + - [DetectorModulePlacement](docs/DetectorModulePlacement.md) - [DetectorPowerState](docs/DetectorPowerState.md) - [DetectorSelection](docs/DetectorSelection.md) - [DetectorSettings](docs/DetectorSettings.md) diff --git a/docs/python_client/docs/DatasetSettings.md b/docs/python_client/docs/DatasetSettings.md index 178b6ab87..3c5530198 100644 --- a/docs/python_client/docs/DatasetSettings.md +++ b/docs/python_client/docs/DatasetSettings.md @@ -45,6 +45,7 @@ Name | Type | Description | Notes **max_spot_count** | **int** | Maximum number of spots that are saved/used for indexing; spots with highest intensity are selected | [optional] [default to 250] **detect_ice_rings** | **bool** | Flag spots as ice rings and reduce their effect on indexing | [optional] **async_start** | **bool** | When set to true, `/start` will not wait for detector and Jungfraujoch to be ready for the measurement. | [optional] [default to False] +**module_geometry** | [**List[DetectorModulePlacement]**](DetectorModulePlacement.md) | Where the detector's modules are for this dataset, as rugnux measures them (`rugnux --panel-refine` writes it as `<prefix>_panels.json`; the file's `module_geometry` array goes here). Each entry names its module by the module's box in the assembled image (`x0`, `y0`, `width`, `height`), in any order; every module must be named once, else `/start` fails with 400. The image is assembled as configured; the placement goes into the start message and the NXmx `module_NN` groups, and the analysis takes it. Omitted or empty: modules as assembled. | [optional] **tokens** | **List[str]** | Bearer tokens that protect this dataset: any number, all equivalent (e.g. a beamline secret and a dataset-specific user secret). While set, the endpoints marked with the `bearerAuth` security scheme require `Authorization: Bearer <token>` and answer 401 otherwise. Every accepted `/start` replaces the previous dataset's tokens; omitted or empty means no protection. Never returned by any endpoint. | [optional] **xray_fluorescence_spectrum** | [**DatasetSettingsXrayFluorescenceSpectrum**](DatasetSettingsXrayFluorescenceSpectrum.md) | | [optional] diff --git a/docs/python_client/docs/DetectorModulePlacement.md b/docs/python_client/docs/DetectorModulePlacement.md new file mode 100644 index 000000000..a7b36722c --- /dev/null +++ b/docs/python_client/docs/DetectorModulePlacement.md @@ -0,0 +1,39 @@ +# DetectorModulePlacement + +One module of the assembled image and where it lies on the detector plane, in pixels of the assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at (ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0), (sx, sy) = (0, 1). + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**x0** | **int** | | +**y0** | **int** | | +**width** | **int** | | +**height** | **int** | | +**ox** | **float** | | +**oy** | **float** | | +**fx** | **float** | | +**fy** | **float** | | +**sx** | **float** | | +**sy** | **float** | | + +## Example + +```python +from jfjoch_client.models.detector_module_placement import DetectorModulePlacement + +# TODO update the JSON string below +json = "{}" +# create an instance of DetectorModulePlacement from a JSON string +detector_module_placement_instance = DetectorModulePlacement.from_json(json) +# print the JSON string representation of the object +print(DetectorModulePlacement.to_json()) + +# convert the object into a dict +detector_module_placement_dict = detector_module_placement_instance.to_dict() +# create an instance of DetectorModulePlacement from a dict +detector_module_placement_from_dict = DetectorModulePlacement.from_dict(detector_module_placement_dict) +``` +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/rugnux/RugnuxStills.cpp b/rugnux/RugnuxStills.cpp index 2abb4fffc..4fa5f5616 100644 --- a/rugnux/RugnuxStills.cpp +++ b/rugnux/RugnuxStills.cpp @@ -121,26 +121,21 @@ struct HeldOutFit { double rms = std::numeric_limits::infinity(); std::vector modules; // the modules as fitted on the fitting half GeometryRefinerResult eval; // the frozen geometry on the other half - DiffractionGeometry geom; }; -// The held-out residuals of the two folds pooled, per module and per resolution shell: each spot's distance to -// its prediction, capped at 2 px as in capped_rms_px. hit counts the spots within 1 px. +// The held-out residuals of the two folds pooled, per module: each spot's distance to its prediction, capped at +// 2 px as in capped_rms_px. struct HeldOutTally { static constexpr double CAP_PX = 2.0; - static inline const std::vector EDGES = {1e9, 4.0, 3.0, 2.5, 2.2, 2.0, 1.8, 1.6, 1.4, 0.0}; struct Sum { double sq = 0; - int n = 0, hit = 0; + int n = 0; double Rms() const { return n > 0 ? std::sqrt(sq / n) : 0.0; } - double Hit() const { return n > 0 ? hit / static_cast(n) : 0.0; } }; - std::vector module; // per module - std::vector> module_shell; // per module, per shell (EDGES[s - 1] > d >= EDGES[s]) - std::vector shell; + std::vector module; HeldOutTally(const std::vector half[2], const HeldOutFit fits[2], size_t nmodules) - : module(nmodules), module_shell(nmodules, std::vector(EDGES.size())), shell(EDGES.size()) { + : module(nmodules) { for (int fit = 0; fit < 2; fit++) { const auto &e = fits[fit].eval; const auto &frames = half[1 - fit]; @@ -153,48 +148,15 @@ struct HeldOutTally { double r = CAP_PX; if (std::isfinite(pr[0]) && std::isfinite(pr[1])) r = std::min(CAP_PX, static_cast(std::hypot(pr[0] - sp.x, pr[1] - sp.y))); - const double d = 1.0 / std::max(1e-6f, fits[fit].geom.DetectorToRecip(sp.x, sp.y).Length()); - size_t sh = 1; - while (sh + 1 < EDGES.size() && d < EDGES[sh]) - sh++; - Add(shell[sh], r); if (sp.module >= 0 && static_cast(sp.module) < nmodules) { - Add(module[sp.module], r); - Add(module_shell[sp.module][sh], r); + module[sp.module].sq += r * r; + module[sp.module].n++; } } } } - static void Add(Sum &s, double r) { - s.sq += r * r; - s.n++; - s.hit += r < 1.0; - } }; -// PANEL STUDY (RUGNUX_PANEL_STUDY): the tally of one hypothesis and each module's shift on the two halves, to -// the log under "PanelStudy". -void LogPanelStudy(Logger &logger, const std::string &label, const HeldOutTally &t, const HeldOutFit fits[2]) { - const auto &E = HeldOutTally::EDGES; - for (size_t sh = 1; sh < E.size(); sh++) - if (t.shell[sh].n > 0) - logger.Info("PanelStudy {} shell {:.2f} {:.2f} n {} rms {:.4f} hit1 {:.4f}", label, E[sh - 1], E[sh], - t.shell[sh].n, t.shell[sh].Rms(), t.shell[sh].Hit()); - for (size_t m = 0; m < t.module.size(); m++) { - if (t.module[m].n == 0 || fits[0].modules.size() <= m || fits[1].modules.size() <= m) - continue; - logger.Info("PanelStudy {} module {} x0 {:.0f} y0 {:.0f} n {} rms {:.4f} hit1 {:.4f} " - "fold0 {:+.3f} {:+.3f} {} fold1 {:+.3f} {:+.3f} {}", label, m, - fits[0].modules[m].x0, fits[0].modules[m].y0, t.module[m].n, t.module[m].Rms(), t.module[m].Hit(), - fits[0].modules[m].dx, fits[0].modules[m].dy, fits[0].modules[m].spots, - fits[1].modules[m].dx, fits[1].modules[m].dy, fits[1].modules[m].spots); - for (size_t sh = 1; sh < E.size(); sh++) - if (t.module_shell[m][sh].n > 0) - logger.Info("PanelStudy {} pshell {} {:.2f} {:.2f} n {} rms {:.4f} hit1 {:.4f}", label, m, E[sh - 1], - E[sh], t.module_shell[m][sh].n, t.module_shell[m][sh].Rms(), t.module_shell[m][sh].Hit()); - } -} - bool SameGeometry(const DiffractionGeometry &a, const DiffractionGeometry &b) { return a.GetBeamX_pxl() == b.GetBeamX_pxl() && a.GetBeamY_pxl() == b.GetBeamY_pxl() && a.GetDetectorDistance_mm() == b.GetDetectorDistance_mm() && a.GetPixelSize_mm() == b.GetPixelSize_mm() @@ -601,7 +563,6 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_ ev.refine_beam_distance = false; ev.modules = f.modules; h.modules = f.modules; - h.geom = g; h.eval = RefineGlobalGeometry(g, f.cell, half[1 - fit], ev); if (h.eval.ok) h.rms = h.eval.capped_rms_px; @@ -618,10 +579,6 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_ const double moved = (moved_f[0].rms + moved_f[1].rms) / 2.0; const HeldOutTally held_t(half, held_f, centres.size()); const HeldOutTally moved_t(half, moved_f, centres.size()); - if (std::getenv("RUGNUX_PANEL_STUDY")) { - LogPanelStudy(logger, "given", held_t, held_f); - LogPanelStudy(logger, "placed", moved_t, moved_f); - } GeometryRefinerResult rm = placement.get(); placement_mark.Stop(); logger.Info("Module placement: {} modules; held-out spot residual (capped rms) {:.3f} px as given, "