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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-03 08:02:20 +01:00
1 parent 0976d5836c
commit 26d751f89c
4 files changed
+48 -50

No files matched your search

+1
View File
@@ -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)
@@ -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, &#x60;/start&#x60; 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&#39;s modules are for this dataset, as rugnux measures them (&#x60;rugnux --panel-refine&#x60; writes it as &#x60;&lt;prefix&gt;_panels.json&#x60;; the file&#39;s &#x60;module_geometry&#x60; array goes here). Each entry names its module by the module&#39;s box in the assembled image (&#x60;x0&#x60;, &#x60;y0&#x60;, &#x60;width&#x60;, &#x60;height&#x60;), in any order; every module must be named once, else &#x60;/start&#x60; fails with 400. The image is assembled as configured; the placement goes into the start message and the NXmx &#x60;module_NN&#x60; 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 &#x60;bearerAuth&#x60; security scheme require &#x60;Authorization: Bearer &lt;token&gt;&#x60; and answer 401 otherwise. Every accepted &#x60;/start&#x60; replaces the previous dataset&#39;s tokens; omitted or empty means no protection. Never returned by any endpoint. | [optional]
**xray_fluorescence_spectrum** | [**DatasetSettingsXrayFluorescenceSpectrum**](DatasetSettingsXrayFluorescenceSpectrum.md) | | [optional]
@@ -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)
+7 -50
View File
@@ -121,26 +121,21 @@ struct HeldOutFit {
double rms = std::numeric_limits<double>::infinity();
std::vector<ModuleShift> 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<double> 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<double>(n) : 0.0; }
};
std::vector<Sum> module; // per module
std::vector<std::vector<Sum>> module_shell; // per module, per shell (EDGES[s - 1] > d >= EDGES[s])
std::vector<Sum> shell;
std::vector<Sum> module;
HeldOutTally(const std::vector<GeomRefineFrame> half[2], const HeldOutFit fits[2], size_t nmodules)
: module(nmodules), module_shell(nmodules, std::vector<Sum>(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<double>(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<size_t>(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, "