Files
Jungfraujoch/process/JFJochProcessCommandLine.cpp
T
leonarski_fandClaude Opus 4.8 9f45935fd3 Scale/merge: rotation -> RotationScaleMerge, stills -> ScaleOnTheFly (fixed)
Consolidate the offline scaling/merging on two engines and delete the old classic
rotation chain:

- ScaleOnTheFly now implements ONLY the fixed-partiality model: it uses each reflection's
  stored partiality as-is (1 for stills, the zeta/erf rocking-curve value already set at
  prediction for rotation). Dropped the Rotation partiality recompute, the unity override,
  the ceres rotation residual, and all wedge/mosaicity refinement.
- Remove Combine3D (CombineRotationObservations): its 3D-combine lives in RotationScaleMerge.
- JFJochProcess / jfjoch_scale: rotation (-P rot3d) goes through RotationScaleMerge for the
  whole self-scale -> 3D combine -> scale-fulls -> merge; stills self-scale with ScaleOnTheFly
  + MergeOnTheFly. RotationScaleMerge does not support external-reference scaling, B-factor
  refinement, an absorption surface or wedge refinement, so those combinations now throw
  instead of silently falling back. Deleted the classic ScaleFulls / AbsorptionSurface /
  SmoothImageScaleG helpers.
- CLI cleanup: drop -w/--wedge (wedge refinement) and --absorption (both tools), and the now
  dead -P values rot and unity (keep fixed | rot3d). Fix the -P round-trip in the reproduced
  command line.

Behaviour on the rotation path is unchanged: the full 18-crystal battery matches the
pre-cleanup metrics (SG/ISa/CC1.2/completeness) exactly on all 15 deterministic crystals.
Non-CUDA build unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 11:41:49 +02:00

138 lines
5.1 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochProcessCommandLine.h"
#include "../common/DiffractionExperiment.h"
#include <sstream>
#include <vector>
namespace {
std::string quote_if_needed(const std::string &s) {
if (s.find_first_of(" \t\"'") == std::string::npos)
return s;
std::string out = "\"";
for (char c: s) {
if (c == '"' || c == '\\')
out += '\\';
out += c;
}
out += '"';
return out;
}
const char *indexing_alg_flag(IndexingAlgorithmEnum a) {
switch (a) {
case IndexingAlgorithmEnum::FFBIDX: return "ffbidx";
case IndexingAlgorithmEnum::FFT: return "fft";
case IndexingAlgorithmEnum::FFTW: return "fftw";
case IndexingAlgorithmEnum::None: return "none";
case IndexingAlgorithmEnum::Auto:
default: return "auto";
}
}
const char *refine_flag(GeomRefinementAlgorithmEnum r) {
switch (r) {
case GeomRefinementAlgorithmEnum::None: return "none";
case GeomRefinementAlgorithmEnum::OrientationOnly: return "orientation";
case GeomRefinementAlgorithmEnum::BeamCenter:
default: return "beam_and_lattice";
}
}
std::string num(double v) {
std::ostringstream o;
o << v;
return o.str();
}
}
std::string JFJochProcessCommandLine(const ProcessConfig &config,
const DiffractionExperiment &experiment,
const std::string &input_file) {
std::vector<std::string> args;
const bool azint = (config.mode == ProcessMode::AzimuthalIntegration);
args.emplace_back(azint ? "jfjoch_azint" : "jfjoch_process");
auto add = [&](const std::string &flag, const std::string &val) {
args.push_back(flag);
args.push_back(val);
};
if (!config.output_prefix.empty())
add("-o", config.output_prefix);
add("-N", std::to_string(config.nthreads));
if (config.start_image != 0)
add("-s", std::to_string(config.start_image));
if (config.end_image >= 0)
add("-e", std::to_string(config.end_image));
if (config.stride != 1)
add("-t", std::to_string(config.stride));
if (azint) {
const auto a = experiment.GetAzimuthalIntegrationSettings();
add("--min-q", num(a.GetLowQ_recipA()));
add("--max-q", num(a.GetHighQ_recipA()));
add("--q-spacing", num(a.GetQSpacing_recipA()));
add("--azimuthal-bins", std::to_string(a.GetAzimuthalBinCount()));
add("--polarization-correction", a.IsPolarizationCorrection() ? "on" : "off");
add("--solid-angle-correction", a.IsSolidAngleCorrection() ? "on" : "off");
} else {
const auto &sf = config.spot_finding;
add("--spot-sigma", num(sf.signal_to_noise_threshold));
add("--spot-threshold", std::to_string(sf.photon_count_threshold));
add("--spot-high-resolution", num(sf.high_resolution_limit));
add("--max-spots", std::to_string(experiment.GetMaxSpotCount()));
const auto idx = experiment.GetIndexingSettings();
add("-X", indexing_alg_flag(idx.GetAlgorithm()));
add("-r", refine_flag(idx.GetGeomRefinementAlgorithm()));
if (const auto sg = experiment.GetSpaceGroupNumber())
add("-S", std::to_string(*sg));
if (const auto uc = experiment.GetUnitCell()) {
std::ostringstream o;
o << uc->a << "," << uc->b << "," << uc->c << "," << uc->alpha << "," << uc->beta << "," << uc->gamma;
add("-C", o.str());
}
if (const auto bw = experiment.GetBandwidthFWHM())
add("--bandwidth", num(*bw));
const auto bragg = experiment.GetBraggIntegrationSettings();
std::ostringstream radii;
radii << bragg.GetR1() << "," << bragg.GetR2() << "," << bragg.GetR3();
add("--integration-radius", radii.str());
if (config.rotation_indexing) {
if (config.two_pass_rotation)
add("-R", std::to_string(config.rotation_indexing_image_count));
else
args.emplace_back("--single-pass-rotation");
if (!config.reuse_rotation_spots)
args.emplace_back("--redo-rotation-spots");
}
if (config.run_scaling) {
args.emplace_back("-M");
const auto sc = experiment.GetScalingSettings();
if (const auto pm = sc.GetPartialityModel())
add("-P", *pm == PartialityModel::Rotation ? "rot3d" : "fixed");
if (!sc.GetMergeFriedel())
args.emplace_back("-A");
if (sc.GetRefineB())
args.emplace_back("-B");
}
}
args.push_back(input_file);
std::ostringstream cmd;
for (size_t i = 0; i < args.size(); i++) {
if (i)
cmd << ' ';
cmd << quote_if_needed(args[i]);
}
return cmd.str();
}