Replace the dead PartialityModel with a force_still_processing flag

With ScaleOnTheFly now fixed-only and rotation routed through RotationScaleMerge, the
PartialityModel enum carried no information (it was always Rotation for rotation processing,
Fixed otherwise, and mirrored combine_3d). Drop it:

- ScalingSettings: `partiality_mode` (PartialityModel) -> `force_still_processing` (bool);
  SetPartialityModel/GetPartialityModel -> ForceStillProcessing/GetForceStillProcessing.
  Remove the enum.
- DiffractionExperiment: drop GetPartialityModel(); the rotation-vs-stills decision is now
  just GetCombine3D() (set by the tool = rotation && !force_still). The wedge getters no longer
  key off the model (dead since wedge refinement was removed).
- jfjoch_process: `-P/--partiality fixed|rot3d` -> `--force-still-processing` (a rotation
  dataset scaled as independent stills). Auto-detect sets combine_3d for rotation data unless
  the flag is given.
- jfjoch_scale: `-P fixed|rot3d` now toggles combine_3d directly (no PartialityModel).
- jfjoch_viewer: the "process as stills" toggle sets ForceStillProcessing(!rotation_mode) -
  UI unchanged, just wired to the new field.

PartialityModel was never in the OpenAPI, so no generated clients change. Rotation path
behaviour is unchanged (lyso 16.4/99.6%/87.3%); --force-still-processing correctly routes to
ScaleOnTheFly. CUDA + non-CUDA + viewer all build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 11:58:34 +02:00
co-authored by Claude Opus 4.8
parent 9f45935fd3
commit b4d3d40cd8
9 changed files with 35 additions and 73 deletions
+2 -22
View File
@@ -1696,30 +1696,10 @@ DiffractionExperiment &DiffractionExperiment::RunNumber(uint64_t input) {
return *this;
}
PartialityModel DiffractionExperiment::GetPartialityModel() const {
if (const auto model = scaling_settings.GetPartialityModel())
return model.value();
if (GetGoniometer().has_value())
return PartialityModel::Rotation;
return PartialityModel::Unity;
}
std::optional<double> DiffractionExperiment::GetRotationWedgeForScaling() const {
// Only makes sense for rotation partiality model
// If ScalingSettings set one, it is takes priority
// otherwise if this is really rotation scan, than actual rotation wedge is given
// otherwise return std::nullopt
if (GetPartialityModel() != PartialityModel::Rotation)
return std::nullopt;
if (scaling_settings.GetRotationWedgeForScaling().has_value())
return scaling_settings.GetRotationWedgeForScaling();
if (GetGoniometer().has_value())
return GetGoniometer()->GetWedge_deg();
return std::nullopt;
return scaling_settings.GetRotationWedgeForScaling();
}
bool DiffractionExperiment::GetRefineRotationWedgeInScaling() const {
if (GetRotationWedgeForScaling().has_value())
return scaling_settings.GetRefineWedge();
return false;
return scaling_settings.GetRotationWedgeForScaling().has_value() && scaling_settings.GetRefineWedge();
}
-1
View File
@@ -417,7 +417,6 @@ public:
bool IsRotationIndexing() const;
PartialityModel GetPartialityModel() const;
std::optional<double> GetRotationWedgeForScaling() const;
bool GetRefineRotationWedgeInScaling() const;
};
+4 -4
View File
@@ -5,8 +5,8 @@
#include "ScalingSettings.h"
ScalingSettings& ScalingSettings::SetPartialityModel(PartialityModel mode) {
partiality_mode = mode;
ScalingSettings& ScalingSettings::ForceStillProcessing(bool input) {
force_still_processing = input;
return *this;
}
@@ -51,8 +51,8 @@ bool ScalingSettings::GetRefineWedge() const {
return refine_wedge;
}
std::optional<PartialityModel> ScalingSettings::GetPartialityModel() const {
return partiality_mode;
bool ScalingSettings::GetForceStillProcessing() const {
return force_still_processing;
}
std::optional<double> ScalingSettings::GetHighResolutionLimit_A() const {
+8 -7
View File
@@ -6,11 +6,12 @@
#include <optional>
#include "JFJochException.h"
enum class PartialityModel { Fixed, Rotation, Unity };
enum class IntensityFormat { Text, mmCIF, MTZ};
class ScalingSettings {
std::optional<PartialityModel> partiality_mode;
// Force per-image stills scaling (ScaleOnTheFly, fixed partiality) even for a rotation dataset,
// instead of the rotation self-scale + 3D combine + merge (RotationScaleMerge). Off by default.
bool force_still_processing = false;
bool refine_b = false;
double max_b = 200.0;
@@ -30,9 +31,9 @@ class ScalingSettings {
double min_cc_for_image = 0.0;
double outlier_reject_nsigma = 0.0; // per-observation merge outlier rejection (XDS/DIALS-style); 0 = off, e.g. 6 enables
// The "-P rot3d" 3D combine: scale per-frame as Rotation, then weight-sum each reflection's
// per-frame partials into one counting-limited full before merging (orthogonal to the partiality
// model, which stays Rotation - see Combine3D).
// Rotation processing (RotationScaleMerge): recompute partiality, weight-sum each reflection's
// per-frame partials into one counting-limited full, scale the fulls and merge. Set by the tool for
// rotation data unless force_still_processing.
bool combine_3d = false;
// Scale fulls: after the rot3d combine, refit a per-frame scale on the combined fulls (XDS
@@ -55,7 +56,7 @@ class ScalingSettings {
bool scaling_regularize = false;
public:
ScalingSettings& SetPartialityModel(PartialityModel mode);
ScalingSettings& ForceStillProcessing(bool input);
ScalingSettings& RefineB(bool input);
ScalingSettings& RefineRotationWedge(bool input);
ScalingSettings& RotationWedgeForScaling(std::optional<double> input);
@@ -92,7 +93,7 @@ public:
[[nodiscard]] bool GetMergeFriedel() const;
[[nodiscard]] std::optional<PartialityModel> GetPartialityModel() const;
[[nodiscard]] bool GetForceStillProcessing() const;
[[nodiscard]] std::optional<double> GetHighResolutionLimit_A() const;
[[nodiscard]] double GetMinPartiality() const;
+1 -2
View File
@@ -440,8 +440,7 @@ ProcessResult JFJochProcess::Run(JFJochProcessObserver *observer) {
// (forced) mosaicity is handled by the recompute. It does NOT support external-reference scaling,
// B-factor refinement, an absorption surface, or wedge refinement - reject those combinations.
const auto &rot_ss = experiment_.GetScalingSettings();
const bool is_rotation = rot_ss.GetCombine3D()
&& experiment_.GetPartialityModel() == PartialityModel::Rotation;
const bool is_rotation = rot_ss.GetCombine3D(); // set for rotation data unless force-still
std::optional<RotationScaleMerge> rsm;
if (is_rotation) {
if (!config_.reference_data.empty() || rot_ss.GetRefineB() || rot_ss.GetAbsorptionIter() > 0
+2 -2
View File
@@ -116,8 +116,8 @@ std::string JFJochProcessCommandLine(const ProcessConfig &config,
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.GetForceStillProcessing())
args.emplace_back("--force-still-processing");
if (!sc.GetMergeFriedel())
args.emplace_back("-A");
if (sc.GetRefineB())
+12 -25
View File
@@ -63,7 +63,7 @@ void print_usage() {
std::cout << " --no-scale-fulls Disable the rot3d scale-fulls refit (it is on by default for rot3d)" << std::endl;
std::cout << " --write-process-h5 Also write the (large) _process.h5 when merging (default: only .mtz/.cif when merging)" << std::endl;
std::cout << " --smooth-g[=deg] rot3d: smooth per-frame scale G over a deg-degree rotation range (XDS DELPHI-like) before the combine (default: 5 for rot3d; 0 = off)" << std::endl;
std::cout << " -P, --partiality <txt> Scaling/merge model fixed|rot3d (default: rot3d for rotation data, fixed for stills). rot3d = RotationScaleMerge (recompute partiality, 3D combine, scale-fulls, merge); fixed = per-image ScaleOnTheFly with the stored partiality" << std::endl;
std::cout << " --force-still-processing Scale a rotation dataset as independent stills (per-image ScaleOnTheFly, fixed partiality) instead of the default RotationScaleMerge (recompute partiality, 3D combine, scale-fulls, merge)" << std::endl;
std::cout << " -A, --anomalous Anomalous mode (don't merge Friedel pairs)" << std::endl;
std::cout << " -B, --refine-bfactor Refine per image B-factor" << std::endl;
std::cout << " --scaling-high-resolution <num> High resolution limit for spot finding (default: no limit)" << std::endl;
@@ -117,6 +117,7 @@ enum {
OPT_NO_SCALE_FULLS,
OPT_WRITE_PROCESS_H5,
OPT_PROCESS_AS_STILLS,
OPT_FORCE_STILL_PROCESSING,
OPT_AZIM_MIN_Q,
OPT_AZIM_MAX_Q,
OPT_AZIM_PHI_BINS
@@ -135,7 +136,7 @@ static option long_options[] = {
{"reference-column", required_argument, nullptr, OPT_REFERENCE_COLUMN},
{"dump-observations", required_argument, nullptr, OPT_DUMP_OBSERVATIONS},
{"space-group", required_argument, nullptr, 'S'},
{"partiality", required_argument, nullptr, 'P'},
{"force-still-processing", no_argument, nullptr, OPT_FORCE_STILL_PROCESSING},
{"anomalous", no_argument, nullptr, 'A'},
{"refine-bfactor", no_argument, nullptr, 'B'},
{"scale-merge", no_argument, nullptr, 'M'},
@@ -339,8 +340,7 @@ int main(int argc, char **argv) {
GeomRefinementAlgorithmEnum refinement_algorithm = GeomRefinementAlgorithmEnum::BeamCenter;
std::optional<IntensityFormat> intensity_format; // --scaling-output override; default lives in ScalingSettings (mmCIF)
PartialityModel partiality_model = PartialityModel::Fixed;
bool partiality_explicit = false; // true once -P is given, so the rotation/stills default does not override it
bool force_still_processing = false; // --force-still-processing: scale a rotation dataset as stills
bool combine_3d = false; // -P rot3d: weight-sum per-frame partials into fulls before merging
float d_min_spot_finding = 1.5;
@@ -357,7 +357,7 @@ int main(int argc, char **argv) {
int opt;
int option_index = 0;
const char *short_opts = "vo:N:s:e:t:R::X:C:z:FABS:MP:r:q:";
const char *short_opts = "vo:N:s:e:t:R::X:C:z:FABS:Mr:q:";
while ((opt = getopt_long(argc, argv, short_opts, long_options, &option_index)) != -1) {
switch (opt) {
@@ -501,19 +501,8 @@ int main(int argc, char **argv) {
case 'S':
space_group_number = atoi(optarg);
break;
case 'P':
if (strcmp(optarg, "fixed") == 0)
partiality_model = PartialityModel::Fixed;
else if (strcmp(optarg, "rot3d") == 0) {
partiality_model = PartialityModel::Rotation;
combine_3d = true;
}
else {
logger.Error("Invalid partiality mode: {}", optarg);
print_usage();
exit(EXIT_FAILURE);
}
partiality_explicit = true;
case OPT_FORCE_STILL_PROCESSING:
force_still_processing = true;
break;
case OPT_SPOT_SIGMA:
sigma_spot_finding = atof(optarg);
@@ -789,12 +778,10 @@ int main(int argc, char **argv) {
"indexing). Use --process-as-stills to treat it as stills.");
}
// Default partiality model: rot3d for rotation processing (Rotation partiality + 3D combine of the
// per-frame partials into fulls), fixed for stills. An explicit -P always wins.
if (!partiality_explicit && rotation_indexing) {
partiality_model = PartialityModel::Rotation;
combine_3d = true; // rot3d
}
// Rotation data goes through RotationScaleMerge (3D combine of the per-frame partials into fulls)
// unless --force-still-processing asks for per-image stills scaling instead.
if (rotation_indexing && !force_still_processing)
combine_3d = true;
// Configure Indexing
IndexingSettings indexing_settings;
@@ -815,7 +802,7 @@ int main(int argc, char **argv) {
const bool scale_fulls = scale_fulls_arg.value_or(combine_3d);
ScalingSettings scaling_settings;
scaling_settings.SetPartialityModel(partiality_model);
scaling_settings.ForceStillProcessing(force_still_processing);
scaling_settings.Combine3D(combine_3d);
scaling_settings.ScaleFulls(scale_fulls);
scaling_settings.SmoothGDegrees(smooth_g_deg_arg.value_or(combine_3d ? SMOOTH_G_DEFAULT_DEG : 0.0));
+5 -9
View File
@@ -107,7 +107,6 @@ int main(int argc, char **argv) {
int64_t scaling_iter = 3;
std::optional<IntensityFormat> intensity_format; // --scaling-output override; default lives in ScalingSettings (mmCIF)
PartialityModel partiality_model = PartialityModel::Fixed;
bool combine_3d = false; // -P rot3d: weight-sum per-frame partials into fulls before merging
std::optional<float> d_min_scale_merge;
@@ -149,13 +148,11 @@ int main(int argc, char **argv) {
break;
case 'P':
if (strcmp(optarg, "fixed") == 0)
partiality_model = PartialityModel::Fixed;
else if (strcmp(optarg, "rot3d") == 0) {
partiality_model = PartialityModel::Rotation;
combine_3d = true;
}
combine_3d = false; // stills: per-image ScaleOnTheFly
else if (strcmp(optarg, "rot3d") == 0)
combine_3d = true; // rotation: RotationScaleMerge
else {
logger.Error("Invalid partiality mode: {}", optarg);
logger.Error("Invalid scaling model (use fixed|rot3d): {}", optarg);
print_usage();
exit(EXIT_FAILURE);
}
@@ -304,7 +301,6 @@ int main(int argc, char **argv) {
experiment.NumTriggers(1);
ScalingSettings scaling_settings;
scaling_settings.SetPartialityModel(partiality_model);
scaling_settings.Combine3D(combine_3d);
if (d_min_scale_merge)
scaling_settings.HighResolutionLimit_A(d_min_scale_merge.value());
@@ -337,7 +333,7 @@ int main(int argc, char **argv) {
// Rotation (rot3d): the dedicated RotationScaleMerge does the whole self-scale -> 3D combine -> merge
// (on the GPU when present). It does not support external-reference scaling, B-factor refinement or
// wedge refinement. Everything else (stills, reference scaling) uses ScaleOnTheFly + MergeOnTheFly.
const bool is_rotation = combine_3d && experiment.GetPartialityModel() == PartialityModel::Rotation;
const bool is_rotation = combine_3d; // -P rot3d
if (is_rotation) {
if (!reference_data.empty() || experiment.GetScalingSettings().GetRefineB()
|| experiment.GetRefineRotationWedgeInScaling()
+1 -1
View File
@@ -585,7 +585,7 @@ void JFJochViewerSettingsDock::ApplyProcessingMode() {
const bool rotation_data = experiment_.GetGoniometer().has_value();
const bool rotation_mode = rotation_data && stills_ && !stills_->isChecked();
indexing_.RotationIndexing(rotation_mode);
scaling_.SetPartialityModel(rotation_mode ? PartialityModel::Rotation : PartialityModel::Fixed);
scaling_.ForceStillProcessing(!rotation_mode);
scaling_.Combine3D(rotation_mode);
scaling_.ScaleFulls(rotation_mode);
EmitSpotFinding(); // carries indexing_ (incl. RotationIndexing) to the worker