800ffecd20
Foundation for making processing a first-class GUI activity: - process/JFJochProcessCommandLine: reconstruct the equivalent jfjoch_process / jfjoch_azint command line from a ProcessConfig + DiffractionExperiment + input path, for handing a job off to a cluster. Unit-tested (full + azint). - viewer/JFJochProcessController: runs one JFJochProcess job off the GUI thread (its own private reader; HDF5 access is globally serialized so it is safe next to the interactive reader) and reports back via queued Qt signals (started/phaseChanged/progress/finished/failed). Cancel() forwards to JFJochProcess::Cancel(). Progress is throttled to ~200 updates per run. The visible processing UI (jobs window + snapshot switcher + converged settings) builds on this. Verified: tests/jfjoch_test [process]; jfjoch_viewer links and builds against JFJochProcess. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
139 lines
5.2 KiB
C++
139 lines
5.2 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::PixelRefine: return "pixelrefine";
|
|
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::Unity ? "unity" : *pm == PartialityModel::Rotation ? "rot" : "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();
|
|
}
|