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:
leonarski_fandClaude Opus 5.5 committed 2026-11-03 07:15:21 +01:00
1 parent ffc990a330
commit 0976d5836c
10 files changed
+155 -95

No files matched your search

+6 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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) |
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+8 -5
View File
@@ -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;