rugnux: stills module placement on by default, decided per module on held-out spots
The placement was opt-in; without it a multi-panel MPCCD loses most of its merge quality (R_split about 3x), and an all-or-none rule (refine only when every panel is populated) gives that up wherever a few outer panels are sparse. Now, by default: - a module is placeable from 100 spots on its frames' lattices (was 500). The 500 gate held outer panels that split-half fits place consistently (0.3-1.3 px agreement on 4-5 px shifts with 120-200 spots per half) and whose held-out spots then fit much better; held, they were the ones the others' move hurt; - the placement is used if it predicts the held-out spots better as a whole (as before), and then each moved module keeps its move only if the held-out spots on it are predicted better; the others go back to where they were given and the placement is refitted with them held. On a 32-module detector with weak, low-resolution data most moved modules predicted their own held-out spots worse while the global residual still improved. --panel-refine keeps every move without the tests; --panel-refine=off keeps the modules as given; =diagnose is the default plus the placement diagnostics. GeometryRefinerSettings::modules_held holds chosen modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4
This commit is contained in:
1 parent
ffc990a330
commit
0976d5836c
10 files changed
+155
-95
No files matched your search
@@ -436,10 +436,14 @@ report), with its cell to process it with `-C`. The cell written is the merged f
|
||||
without a given cell.
|
||||
|
||||
**Detector modules.** Where the detector is described module by module
|
||||
([Module geometry](RUGNUX_FORMATS.md#module-geometry)), with `--panel-refine` the same refinement places each module with
|
||||
([Module geometry](RUGNUX_FORMATS.md#module-geometry)), the same refinement places each module with
|
||||
enough spots on its frames' lattices as a rigid block - a shift and an in-plane turn - where that
|
||||
predicts held-out spots better: the strong frames are split in half, each half fitted with the modules
|
||||
fixed and with them placed, and scored on the other. The modules move only relative to one another: a
|
||||
fixed and with them placed, and scored on the other. The placement is used if it predicts the held-out
|
||||
spots better as a whole, and then each module keeps its move only if the held-out spots on that module
|
||||
are predicted better; the others go back to where they were given and the placement is fitted again.
|
||||
On a detector of many modules the outer ones, reached only by a few strong high-resolution spots, are
|
||||
the ones this decides on. `--panel-refine` keeps every move without the tests, `--panel-refine=off` none. The modules move only relative to one another: a
|
||||
gauge holds their spot-weighted mean shift, turn and outward push, and with the detector tilt refined
|
||||
also the two tilt-like gradients across the detector, at zero, so the beam centre, distance, tilt and
|
||||
crystal orientations carry what all modules share. A coarse search of each module's position comes
|
||||
|
||||
@@ -25,8 +25,8 @@ has any, its statistics (`/statistics/data_collection`, `/result/scan`), buffere
|
||||
and answer `401` otherwise; `/statistics` omits its `measurement` block instead. See
|
||||
[Security](SECURITY.md#3-authenticated-read-access--per-dataset-bearer-tokens).
|
||||
|
||||
`/start` also takes an optional `module_geometry`: where the JUNGFRAU modules are, as `rugnux
|
||||
--panel-refine` measures them on an earlier run (the `module_geometry` array of its `<prefix>_panels.json`).
|
||||
`/start` also takes an optional `module_geometry`: where the JUNGFRAU modules are, as `rugnux`
|
||||
measures them on an earlier run (the `module_geometry` array of its `<prefix>_panels.json`).
|
||||
Each entry names its module by the module's box in the assembled image, so the order does not matter, and
|
||||
a list that does not name exactly the detector's modules is refused with `400`. The image is assembled as
|
||||
configured; the placement goes into the start message, the master file's `module_NN` groups and the
|
||||
|
||||
+1
-1
@@ -362,7 +362,7 @@ Defaults are taken from the input file; override them to reprocess with a correc
|
||||
| `--crystfel-geometry <file>` | Place the panels of an HDF5/CXI input by a CrystFEL geometry file instead of the file's own metadata: module geometry, camera length, mask arrays and bad regions (optional; [Serial XFEL inputs](RUGNUX_FORMATS.md#serial-xfel-inputs)). Photon energy, camera length and - where the geometry names no mask array - the masks otherwise come from the data files (Cheetah CXI, SACLA) or the command line; gain and saturation are measured. Tilted panels, `dimN` layouts, per-panel data and detector shifts are refused |
|
||||
| `--module-geometry <file>` | Where the detector's modules are: an NXmx file with one NXdetector_module per module (what a run writes as `<prefix>_modules.h5`) or a CrystFEL geometry file whose panels are laid on the image. Overrides the input's own |
|
||||
| `--panels <file.json>` | Place the modules as a module placement file says (`<prefix>_panels.json` of an earlier run; [Module placement file](RUGNUX_FORMATS.md#module-placement-file-prefix_panelsjson)). Entries name modules by their box in the image; one that matches no module, or a module left out, is an error |
|
||||
| `--panel-refine[=diagnose]` | Stills: place each module as a rigid block in the geometry bundle adjustment, where that predicts held-out spots better, and use the placement from there on (default: off - modules stay where they were given, a CrystFEL `.geom` included). The placement is written to `<prefix>_modules.h5` and `<prefix>_panels.json`. `=diagnose` also runs a set of tests on the placement and writes them to the log |
|
||||
| `--panel-refine[=off\|diagnose]` | Stills on a detector of several modules: by default the geometry bundle adjustment places each module as a rigid block where that predicts held-out spots better - the whole placement must beat the given one, and each module keeps its move only where its own held-out spots are predicted better, the others staying where they were given. `--panel-refine` keeps every move the spots support, without these tests; `=off` keeps the modules as given (a CrystFEL `.geom` included). A placement used is written to `<prefix>_modules.h5` and `<prefix>_panels.json`. `=diagnose` also runs a set of tests on the placement and writes them to the log |
|
||||
| `--mask-good [<file>:]<dataset>[@<bits>]` / `--mask-bad [<file>:]<dataset>[@<bits>]` | HDF5/CXI stills: a mask array in the frames' stored layout, in `<file>` or in each data file; usable only where non-zero (or with all `<bits>`) / unusable where non-zero (or with any `<bits>`). Repeatable ([Serial XFEL inputs](RUGNUX_FORMATS.md#serial-xfel-inputs)) |
|
||||
| `--rot1 <num>` | PONI detector rotation 1 (rad). On stills, `--rot1` or `--rot2` holds the tilt (not refined); without them stills refine the tilt where held-out frames agree |
|
||||
| `--rot2 <num>` | PONI detector rotation 2 (rad) |
|
||||
|
||||
@@ -152,8 +152,8 @@ geometry, never from the image:
|
||||
Spots are mapped from the image onto the plane, and predictions from the plane back onto the image for
|
||||
integration, so the image itself is never re-assembled. With a cell given, the stills geometry refinement
|
||||
can place each module with enough spots on its frames' lattices as a rigid block (shift and in-plane
|
||||
turn, `--panel-refine`, off by default: without it the modules stay where they were given, a CrystFEL
|
||||
`.geom`'s panels included) where that predicts held-out spots better; modules move only relative to one
|
||||
turn; `--panel-refine=off` keeps them where they were given, a CrystFEL `.geom`'s panels included)
|
||||
where that predicts held-out spots better, module by module; modules move only relative to one
|
||||
another, the beam centre, distance and crystal orientations carrying what all of them share. The
|
||||
placement is used from there on, and written to `<prefix>_modules.h5` (read back with
|
||||
`--module-geometry`) and `<prefix>_panels.json` (read back with `--panels`, and taken by the broker's
|
||||
|
||||
@@ -324,8 +324,8 @@ Absorption is corrected only by the empirical surface a rotation run fits and ke
|
||||
[The results report](RUGNUX_REPORT.md#the-results-report).
|
||||
- `<prefix>_log.txt` — the full timestamped log of the run, at debug level, written by every run with
|
||||
an output prefix; send it with the report when asking about a run.
|
||||
- `<prefix>_modules.h5`, `<prefix>_panels.json` — serial stills on a detector of several modules, with
|
||||
`--panel-refine`: where the geometry refinement placed each module, readable back with
|
||||
- `<prefix>_modules.h5`, `<prefix>_panels.json` — serial stills on a detector of several modules, where
|
||||
the geometry refinement placed the modules: where it placed each one, readable back with
|
||||
`--module-geometry` / `--panels` (the JSON also by the broker's `/start`; see
|
||||
[Module geometry](RUGNUX_FORMATS.md#module-geometry)).
|
||||
- `<prefix>_plot.txt` — **the per-image table**, one row per processed image, whitespace-separated,
|
||||
|
||||
@@ -435,7 +435,8 @@ GeometryRefinerResult RefineGlobalGeometry(const DiffractionGeometry &nominal_ge
|
||||
std::vector<bool> refined(modules.size(), false);
|
||||
int refined_count = 0;
|
||||
for (size_t m = 0; m < modules.size(); ++m)
|
||||
if (settings.refine_modules && module_spots[m] >= settings.min_module_spots) {
|
||||
if (settings.refine_modules && module_spots[m] >= settings.min_module_spots
|
||||
&& !(m < settings.modules_held.size() && settings.modules_held[m])) {
|
||||
refined[m] = true;
|
||||
refined_count++;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,9 @@ struct GeometryRefinerSettings {
|
||||
// go back to where they were given (no move), whatever their start.
|
||||
std::vector<ModuleShift> modules;
|
||||
bool refine_modules = false;
|
||||
int min_module_spots = 500;
|
||||
int min_module_spots = 100;
|
||||
// Modules held where they were given whatever their spots (as modules; empty: none held).
|
||||
std::vector<bool> modules_held;
|
||||
// Refine the detector tilt (PONI rot1, rot2), within 2 degrees of the nominal one, together with the beam,
|
||||
// the distance and (with refine_modules) the modules. The beam is then fitted as the direct beam, and its
|
||||
// box (beam_range_px) is about where the nominal geometry puts the direct beam: in PONI terms a tilt moves
|
||||
|
||||
+4
-1
@@ -168,8 +168,11 @@ struct ProcessConfig {
|
||||
// Within that bundle adjustment, a detector of several modules (the experiment's DetectorModules: the
|
||||
// file's, the reader's panels, or --module-geometry) has each module placed as a rigid block, where
|
||||
// moving them predicts the spots better on frames the move was not fitted to; the placement found is
|
||||
// used from there on (--panel-refine). Off, the modules stay where they were given.
|
||||
// used from there on; each module keeps its move only where that predicts its own held-out spots better.
|
||||
// Off (--panel-refine=off), the modules stay where they were given.
|
||||
bool refine_modules = false;
|
||||
// Every module the spots place keeps its move, without the held-out tests (--panel-refine).
|
||||
bool force_modules = false;
|
||||
// With it, a run of tests on the placement (ModuleDiagnostics.h) goes to the log (--panel-refine=diagnose).
|
||||
bool module_diagnostics = false;
|
||||
|
||||
|
||||
+125
-78
@@ -124,49 +124,75 @@ struct HeldOutFit {
|
||||
DiffractionGeometry geom;
|
||||
};
|
||||
|
||||
// PANEL STUDY (RUGNUX_PANEL_STUDY): the held-out residual of each fold per module and per resolution shell,
|
||||
// and each module's shift on the two halves, to the log under "PanelStudy".
|
||||
void LogPanelStudy(Logger &logger, const std::string &label, const std::vector<GeomRefineFrame> half[2],
|
||||
const HeldOutFit fits[2]) {
|
||||
constexpr double CAP_PX = 2.0;
|
||||
const std::vector<double> edges = {1e9, 4.0, 3.0, 2.5, 2.2, 2.0, 1.8, 1.6, 1.4, 0.0};
|
||||
const size_t nm = fits[0].modules.size();
|
||||
std::vector<double> m_sq(nm, 0), s_sq(edges.size(), 0);
|
||||
std::vector<int> m_n(nm, 0), m_hit(nm, 0), s_n(edges.size(), 0), s_hit(edges.size(), 0);
|
||||
for (int fit = 0; fit < 2; fit++) {
|
||||
const auto &e = fits[fit].eval;
|
||||
const auto &frames = half[1 - fit];
|
||||
if (e.predicted_px.size() != frames.size())
|
||||
continue;
|
||||
for (size_t i = 0; i < frames.size(); i++)
|
||||
for (size_t j = 0; j < frames[i].spots.size() && j < e.predicted_px[i].size(); j++) {
|
||||
const auto &sp = frames[i].spots[j];
|
||||
const auto &pr = e.predicted_px[i][j];
|
||||
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++;
|
||||
s_sq[sh] += r * r; s_n[sh]++; s_hit[sh] += r < 1.0;
|
||||
if (sp.module >= 0 && static_cast<size_t>(sp.module) < nm) {
|
||||
m_sq[sp.module] += r * r; m_n[sp.module]++; m_hit[sp.module] += r < 1.0;
|
||||
// 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.
|
||||
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;
|
||||
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;
|
||||
|
||||
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()) {
|
||||
for (int fit = 0; fit < 2; fit++) {
|
||||
const auto &e = fits[fit].eval;
|
||||
const auto &frames = half[1 - fit];
|
||||
if (e.predicted_px.size() != frames.size())
|
||||
continue;
|
||||
for (size_t i = 0; i < frames.size(); i++)
|
||||
for (size_t j = 0; j < frames[i].spots.size() && j < e.predicted_px[i].size(); j++) {
|
||||
const auto &sp = frames[i].spots[j];
|
||||
const auto &pr = e.predicted_px[i][j];
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
for (size_t sh = 1; sh < edges.size(); sh++)
|
||||
if (s_n[sh] > 0)
|
||||
logger.Info("PanelStudy {} shell {:.2f} {:.2f} n {} rms {:.4f} hit1 {:.4f}", label, edges[sh - 1], edges[sh],
|
||||
s_n[sh], std::sqrt(s_sq[sh] / s_n[sh]), s_hit[sh] / static_cast<double>(s_n[sh]));
|
||||
for (size_t m = 0; m < nm; m++)
|
||||
if (m_n[m] > 0)
|
||||
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, m_n[m], std::sqrt(m_sq[m] / m_n[m]),
|
||||
m_hit[m] / static_cast<double>(m_n[m]),
|
||||
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);
|
||||
}
|
||||
|
||||
bool SameGeometry(const DiffractionGeometry &a, const DiffractionGeometry &b) {
|
||||
@@ -559,13 +585,12 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_
|
||||
});
|
||||
|
||||
// Held-out residual of the geometry fitted on half `fit`, scored on every spot of the other half.
|
||||
auto held_out = [&](const std::vector<ModuleShift> &modules, bool refine, int fit, int min_spots) {
|
||||
auto held_out = [&](const std::vector<ModuleShift> &modules, bool refine, int fit) {
|
||||
HeldOutFit h;
|
||||
GeometryRefinerSettings sf = gr_settings;
|
||||
sf.num_threads = std::max(1, config_.nthreads / 8);
|
||||
sf.modules = modules;
|
||||
sf.refine_modules = refine;
|
||||
sf.min_module_spots = min_spots;
|
||||
const auto f = RefineGlobalGeometry(nominal, *cell, half[fit], sf);
|
||||
if (!f.ok)
|
||||
return h;
|
||||
@@ -582,55 +607,77 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_
|
||||
h.rms = h.eval.capped_rms_px;
|
||||
return h;
|
||||
};
|
||||
const int min_spots = gr_settings.min_module_spots;
|
||||
std::future<HeldOutFit> held_fits[2], moved_fits[2];
|
||||
for (int fit = 0; fit < 2; fit++) {
|
||||
held_fits[fit] = std::async(std::launch::async, held_out, std::cref(centres), false, fit, min_spots);
|
||||
moved_fits[fit] = std::async(std::launch::async, held_out, std::cref(start), true, fit, min_spots);
|
||||
held_fits[fit] = std::async(std::launch::async, held_out, std::cref(centres), false, fit);
|
||||
moved_fits[fit] = std::async(std::launch::async, held_out, std::cref(start), true, fit);
|
||||
}
|
||||
HeldOutFit held_f[2] = {held_fits[0].get(), held_fits[1].get()};
|
||||
HeldOutFit moved_f[2] = {moved_fits[0].get(), moved_fits[1].get()};
|
||||
const HeldOutFit held_f[2] = {held_fits[0].get(), held_fits[1].get()};
|
||||
const HeldOutFit moved_f[2] = {moved_fits[0].get(), moved_fits[1].get()};
|
||||
const double held = (held_f[0].rms + held_f[1].rms) / 2.0;
|
||||
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", half, held_f);
|
||||
LogPanelStudy(logger, "placed", half, moved_f);
|
||||
HeldOutFit low_f[2] = {held_out(start, true, 0, 100), held_out(start, true, 1, 100)};
|
||||
LogPanelStudy(logger, "placed100", half, low_f);
|
||||
logger.Info("PanelStudy summary given {:.4f} placed {:.4f} placed100 {:.4f}", held, moved,
|
||||
(low_f[0].rms + low_f[1].rms) / 2.0);
|
||||
LogPanelStudy(logger, "given", held_t, held_f);
|
||||
LogPanelStudy(logger, "placed", moved_t, moved_f);
|
||||
}
|
||||
const GeometryRefinerResult rm = placement.get();
|
||||
GeometryRefinerResult rm = placement.get();
|
||||
placement_mark.Stop();
|
||||
logger.Info("Module placement: {} modules; held-out spot residual (capped rms) {:.3f} px as given, "
|
||||
"{:.3f} px with each placed", centres.size(), held, moved);
|
||||
|
||||
if (moved < held) {
|
||||
if (rm.ok) {
|
||||
for (size_t m = 0; m < rm.modules.size(); m++) {
|
||||
const auto &ms = rm.modules[m];
|
||||
logger.Info("Module {} at ({:.0f}, {:.0f}): shift ({:+.2f}, {:+.2f}) px, turn {:+.3f} deg, {} spots",
|
||||
m, ms.x0, ms.y0, ms.dx, ms.dy, ms.rot_rad * 180.0 / PI, ms.spots);
|
||||
// Each module the placement moved keeps its move only where the spots on it, on the frames its move
|
||||
// was not fitted to, are predicted better moved than as given; the others go back to where they were
|
||||
// given and the placement is fitted again without them. --panel-refine keeps every move.
|
||||
bool apply = rm.ok && (config_.force_modules || moved < held);
|
||||
if (apply && !config_.force_modules) {
|
||||
std::vector<bool> hold(centres.size(), false);
|
||||
int held_back = 0;
|
||||
for (size_t m = 0; m < rm.modules.size(); m++) {
|
||||
const bool moved_m = rm.modules[m].dx != 0 || rm.modules[m].dy != 0 || rm.modules[m].rot_rad != 0;
|
||||
if (moved_m && !(moved_t.module[m].Rms() < held_t.module[m].Rms())) {
|
||||
hold[m] = true;
|
||||
held_back++;
|
||||
logger.Info("Module {} at ({:.0f}, {:.0f}): held as given - held-out residual {:.3f} px placed, "
|
||||
"{:.3f} px as given ({} spots)", m, centres[m].x0, centres[m].y0,
|
||||
moved_t.module[m].Rms(), held_t.module[m].Rms(), moved_t.module[m].n);
|
||||
}
|
||||
// The move is a step from where the modules were given; the experiment now has them there,
|
||||
// so every spot found and every prediction integrated from here on uses it. The image is
|
||||
// not touched.
|
||||
if (config_.module_diagnostics) {
|
||||
ModuleDiagnosticsInput diag{nominal, *cell, placing, centres, start, st, rm,
|
||||
experiment_.GetDetectorSetup().GetSensorMaterial(),
|
||||
experiment_.GetDetectorSetup().GetSensorThickness_um()};
|
||||
RunModuleDiagnostics(diag);
|
||||
}
|
||||
module_shifts_ = rm.modules;
|
||||
if (stills_tilt_bundle_)
|
||||
stills_tilt_bundle_ = StillsTiltBundle{placing, rm.modules};
|
||||
DetectorModules placed = given;
|
||||
placed.Apply(rm.modules);
|
||||
experiment_.Modules(placed);
|
||||
r = rm;
|
||||
logger.Info("Module placement: modules placed (median residual {:.3f} px)", rm.median_residual_px);
|
||||
}
|
||||
if (held_back > 0) {
|
||||
GeometryRefinerSettings again = st;
|
||||
again.num_threads = config_.nthreads;
|
||||
again.modules_held = hold;
|
||||
rm = RefineGlobalGeometry(nominal, *cell, placing, again);
|
||||
apply = rm.ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (apply) {
|
||||
for (size_t m = 0; m < rm.modules.size(); m++) {
|
||||
const auto &ms = rm.modules[m];
|
||||
logger.Info("Module {} at ({:.0f}, {:.0f}): shift ({:+.2f}, {:+.2f}) px, turn {:+.3f} deg, {} spots",
|
||||
m, ms.x0, ms.y0, ms.dx, ms.dy, ms.rot_rad * 180.0 / PI, ms.spots);
|
||||
}
|
||||
// The move is a step from where the modules were given; the experiment now has them there,
|
||||
// so every spot found and every prediction integrated from here on uses it. The image is
|
||||
// not touched.
|
||||
if (config_.module_diagnostics) {
|
||||
ModuleDiagnosticsInput diag{nominal, *cell, placing, centres, start, st, rm,
|
||||
experiment_.GetDetectorSetup().GetSensorMaterial(),
|
||||
experiment_.GetDetectorSetup().GetSensorThickness_um()};
|
||||
RunModuleDiagnostics(diag);
|
||||
}
|
||||
module_shifts_ = rm.modules;
|
||||
if (stills_tilt_bundle_)
|
||||
stills_tilt_bundle_ = StillsTiltBundle{placing, rm.modules};
|
||||
DetectorModules placed = given;
|
||||
placed.Apply(rm.modules);
|
||||
experiment_.Modules(placed);
|
||||
r = rm;
|
||||
logger.Info("Module placement: modules placed (median residual {:.3f} px)", rm.median_residual_px);
|
||||
} else
|
||||
logger.Info("Module placement: modules kept as given");
|
||||
}
|
||||
|
||||
logger.Info("Geometry refinement: beam ({:.2f}, {:.2f}) -> ({:.2f}, {:.2f}) px, distance {:.4f} -> {:.4f} mm",
|
||||
|
||||
@@ -978,7 +978,8 @@ struct RugnuxCli {
|
||||
bool beam_center_check = true; // --beam-center-check[=off]; on by default
|
||||
std::optional<int> refine_geometry; // --refine-geometry[=N]: stills global geometry-refinement pass
|
||||
bool refine_geometry_disabled = false; // --refine-geometry=off: opt out of the stills default-on
|
||||
bool refine_modules = false; // --panel-refine
|
||||
bool refine_modules = true; // placed where held-out spots say so; --panel-refine=off: as given
|
||||
bool force_modules = false; // --panel-refine: every placeable module moved, no held-out tests
|
||||
bool module_diagnostics = false; // --panel-refine=diagnose
|
||||
std::string module_geometry_file; // --module-geometry <file>
|
||||
std::string panels_file; // --panels <file.json>
|
||||
@@ -1178,13 +1179,14 @@ int RugnuxCli::Parse(int argc, char **argv) {
|
||||
break;
|
||||
}
|
||||
case OPT_REFINE_MODULES:
|
||||
if (optarg && std::string(optarg) != "diagnose") {
|
||||
logger.Error("--panel-refine takes no value or =diagnose, not {}", optarg);
|
||||
if (optarg && std::string(optarg) != "diagnose" && std::string(optarg) != "off") {
|
||||
logger.Error("--panel-refine takes no value, =off or =diagnose, not {}", optarg);
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
refine_modules = true;
|
||||
module_diagnostics = optarg != nullptr;
|
||||
refine_modules = !optarg || std::string(optarg) != "off";
|
||||
force_modules = !optarg;
|
||||
module_diagnostics = optarg && std::string(optarg) == "diagnose";
|
||||
break;
|
||||
case OPT_MODULE_GEOMETRY:
|
||||
module_geometry_file = optarg;
|
||||
@@ -3654,6 +3656,7 @@ int RugnuxInputRun::PrepareRugnuxInput() {
|
||||
config.forced_rotation_lattice = forced_rotation_lattice;
|
||||
config.refine_geometry = refine_geometry;
|
||||
config.refine_modules = refine_modules;
|
||||
config.force_modules = force_modules;
|
||||
config.module_diagnostics = module_diagnostics;
|
||||
config.run_scaling = run_scaling;
|
||||
config.scaling_iter = scaling_iter;
|
||||
|
||||
Reference in new issue
Block a user