PixelRefine: Claude fixed my bugs
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 11m3s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m15s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 12m13s
Build Packages / build:rpm (rocky8) (push) Successful in 13m9s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m13s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m3s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 14m5s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m2s
Build Packages / XDS test (durin plugin) (push) Successful in 8m48s
Build Packages / Generate python client (push) Successful in 18s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (ubuntu2404) (push) Successful in 10m13s
Build Packages / Build documentation (push) Successful in 42s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m40s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 11m11s
Build Packages / build:rpm (rocky9) (push) Successful in 12m42s
Build Packages / DIALS test (push) Successful in 12m21s
Build Packages / Unit tests (push) Successful in 58m57s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 11m3s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m15s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 12m13s
Build Packages / build:rpm (rocky8) (push) Successful in 13m9s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m13s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m3s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 14m5s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m2s
Build Packages / XDS test (durin plugin) (push) Successful in 8m48s
Build Packages / Generate python client (push) Successful in 18s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (ubuntu2404) (push) Successful in 10m13s
Build Packages / Build documentation (push) Successful in 42s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m40s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 11m11s
Build Packages / build:rpm (rocky9) (push) Successful in 12m42s
Build Packages / DIALS test (push) Successful in 12m21s
Build Packages / Unit tests (push) Successful in 58m57s
This commit is contained in:
@@ -319,6 +319,56 @@ PixelRefine::PixelRefine(const DiffractionExperiment &experiment,
|
||||
reference_data[hkl_key_generator(ref)] = ref.I;
|
||||
}
|
||||
|
||||
void PixelRefine::BuildParameterBlocks(const PixelRefineData &data,
|
||||
double beam[2], double &dist_mm,
|
||||
double detector_rot[2], double rot_vec[3],
|
||||
double latt_vec0[3], double latt_vec1[3], double latt_vec2[3]) const {
|
||||
beam[0] = data.geom.GetBeamX_pxl();
|
||||
beam[1] = data.geom.GetBeamY_pxl();
|
||||
dist_mm = data.geom.GetDetectorDistance_mm();
|
||||
detector_rot[0] = data.geom.GetPoniRot1_rad();
|
||||
detector_rot[1] = data.geom.GetPoniRot2_rad();
|
||||
rot_vec[0] = 1.0; rot_vec[1] = 0.0; rot_vec[2] = 0.0;
|
||||
if (auto axis = data.geom.GetRotation()) {
|
||||
rot_vec[0] = axis->GetAxis().x;
|
||||
rot_vec[1] = axis->GetAxis().y;
|
||||
rot_vec[2] = axis->GetAxis().z;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
latt_vec0[i] = latt_vec1[i] = latt_vec2[i] = 0.0;
|
||||
|
||||
double beta = data.latt.GetUnitCell().beta;
|
||||
switch (data.crystal_system) {
|
||||
case gemmi::CrystalSystem::Orthorhombic:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
break;
|
||||
case gemmi::CrystalSystem::Tetragonal:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
latt_vec1[0] = (latt_vec1[0] + latt_vec1[1]) / 2.0;
|
||||
break;
|
||||
case gemmi::CrystalSystem::Cubic:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
latt_vec1[0] = (latt_vec1[0] + latt_vec1[1] + latt_vec1[2]) / 3.0;
|
||||
break;
|
||||
case gemmi::CrystalSystem::Hexagonal:
|
||||
LatticeToRodriguesAndLengths_Hex(data.latt, latt_vec0, latt_vec1);
|
||||
break;
|
||||
case gemmi::CrystalSystem::Monoclinic:
|
||||
LatticeToRodriguesLengthsBeta_Mono(data.latt, latt_vec0, latt_vec1, beta);
|
||||
latt_vec2[0] = beta;
|
||||
break;
|
||||
default: {
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
const auto uc = data.latt.GetUnitCell();
|
||||
latt_vec2[0] = uc.alpha * M_PI / 180.0;
|
||||
latt_vec2[1] = uc.beta * M_PI / 180.0;
|
||||
latt_vec2[2] = uc.gamma * M_PI / 180.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PixelRefine::Run(const T *image,
|
||||
const AzimuthalIntegrationProfile &profile,
|
||||
@@ -340,7 +390,10 @@ void PixelRefine::Run(const T *image,
|
||||
const auto azim_std = profile.GetStd();
|
||||
const auto &pixel_to_bin = mapping.GetPixelToBin();
|
||||
const auto &corrections = mapping.Corrections();
|
||||
const int azim_bin_count = mapping.GetAzimuthalBinCount();
|
||||
// pixel_to_bin stores the *full* bin index (azimuthal_sector * q_bins + q_bin),
|
||||
// so the valid range is the total number of bins, i.e. the profile size - NOT
|
||||
// GetAzimuthalBinCount() (which is only the number of azimuthal sectors).
|
||||
const int total_bin_count = static_cast<int>(azim_result.size());
|
||||
|
||||
const double angle_rad = data.angle_deg * M_PI / 180.0;
|
||||
const int radius = data.shoebox_radius;
|
||||
@@ -392,11 +445,15 @@ void PixelRefine::Run(const T *image,
|
||||
.PoniRot1_rad(data.geom.GetPoniRot1_rad())
|
||||
.PoniRot2_rad(data.geom.GetPoniRot2_rad());
|
||||
|
||||
prediction.Calc(exp_iter, data.latt, settings_prediction);
|
||||
const int nrefl = prediction.Calc(exp_iter, data.latt, settings_prediction);
|
||||
|
||||
// ---- 2. Collect per-reflection shoebox pixels -------------------------
|
||||
// GetReflections() returns the full pre-sized buffer; only the first
|
||||
// nrefl entries are valid for this image (the rest are stale/zeroed).
|
||||
groups.clear();
|
||||
for (const auto &refl : prediction.GetReflections()) {
|
||||
const auto &predicted = prediction.GetReflections();
|
||||
for (int ri = 0; ri < nrefl; ++ri) {
|
||||
const auto &refl = predicted[ri];
|
||||
const auto hkl = hkl_key_generator(refl);
|
||||
if (!reference_data.contains(hkl))
|
||||
continue;
|
||||
@@ -421,10 +478,10 @@ void PixelRefine::Run(const T *image,
|
||||
const size_t npixel = xpixel * y + x;
|
||||
const int azim_bin = pixel_to_bin[npixel];
|
||||
|
||||
// Skip pixels not mapped to an azimuthal bin or carrying a
|
||||
// sentinel (masked / saturated) value. We assume the pixel
|
||||
// mask is already applied upstream.
|
||||
if (azim_bin >= azim_bin_count)
|
||||
// Skip pixels not mapped to a bin or carrying a sentinel
|
||||
// (masked / saturated) value. We assume the pixel mask is
|
||||
// already applied upstream.
|
||||
if (azim_bin >= total_bin_count)
|
||||
continue;
|
||||
if (image[npixel] == std::numeric_limits<T>::max())
|
||||
continue;
|
||||
@@ -464,47 +521,8 @@ void PixelRefine::Run(const T *image,
|
||||
return;
|
||||
|
||||
// ---- 3. Set up parameter blocks (geometry part mirrors XtalOptimizer) -
|
||||
beam[0] = data.geom.GetBeamX_pxl();
|
||||
beam[1] = data.geom.GetBeamY_pxl();
|
||||
dist_mm = data.geom.GetDetectorDistance_mm();
|
||||
detector_rot[0] = data.geom.GetPoniRot1_rad();
|
||||
detector_rot[1] = data.geom.GetPoniRot2_rad();
|
||||
rot_vec[0] = 1.0; rot_vec[1] = 0.0; rot_vec[2] = 0.0;
|
||||
if (auto axis = data.geom.GetRotation()) {
|
||||
rot_vec[0] = axis->GetAxis().x;
|
||||
rot_vec[1] = axis->GetAxis().y;
|
||||
rot_vec[2] = axis->GetAxis().z;
|
||||
}
|
||||
|
||||
double beta = data.latt.GetUnitCell().beta;
|
||||
switch (data.crystal_system) {
|
||||
case gemmi::CrystalSystem::Orthorhombic:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
break;
|
||||
case gemmi::CrystalSystem::Tetragonal:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
latt_vec1[0] = (latt_vec1[0] + latt_vec1[1]) / 2.0;
|
||||
break;
|
||||
case gemmi::CrystalSystem::Cubic:
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
latt_vec1[0] = (latt_vec1[0] + latt_vec1[1] + latt_vec1[2]) / 3.0;
|
||||
break;
|
||||
case gemmi::CrystalSystem::Hexagonal:
|
||||
LatticeToRodriguesAndLengths_Hex(data.latt, latt_vec0, latt_vec1);
|
||||
break;
|
||||
case gemmi::CrystalSystem::Monoclinic:
|
||||
LatticeToRodriguesLengthsBeta_Mono(data.latt, latt_vec0, latt_vec1, beta);
|
||||
latt_vec2[0] = beta;
|
||||
break;
|
||||
default: {
|
||||
LatticeToRodriguesAndLengths_GS(data.latt, latt_vec0, latt_vec1);
|
||||
const auto uc = data.latt.GetUnitCell();
|
||||
latt_vec2[0] = uc.alpha * M_PI / 180.0;
|
||||
latt_vec2[1] = uc.beta * M_PI / 180.0;
|
||||
latt_vec2[2] = uc.gamma * M_PI / 180.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BuildParameterBlocks(data, beam, dist_mm, detector_rot, rot_vec,
|
||||
latt_vec0, latt_vec1, latt_vec2);
|
||||
|
||||
// ---- 4. Build the problem ---------------------------------------------
|
||||
ceres::Problem problem;
|
||||
@@ -709,6 +727,114 @@ void PixelRefine::Run(const T *image,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<float> PixelRefine::PredictImage(const AzimuthalIntegrationProfile &profile,
|
||||
BraggPrediction &prediction,
|
||||
const PixelRefineData &data,
|
||||
bool include_background) const {
|
||||
std::vector<float> img(xpixel * ypixel, 0.0f);
|
||||
|
||||
const double lambda = data.geom.GetWavelength_A();
|
||||
const double pixel_size = data.geom.GetPixelSize_mm();
|
||||
const auto azim_result = profile.GetResult();
|
||||
const auto &pixel_to_bin = mapping.GetPixelToBin();
|
||||
const auto &corrections = mapping.Corrections();
|
||||
const int total_bin_count = static_cast<int>(azim_result.size());
|
||||
const double angle_rad = data.angle_deg * M_PI / 180.0;
|
||||
const int radius = data.shoebox_radius;
|
||||
const double bw = data.bandwidth;
|
||||
|
||||
auto recip_area = [&](double x, double y) -> double {
|
||||
const Coord qx = data.geom.DetectorToRecip(x + 0.5, y) - data.geom.DetectorToRecip(x - 0.5, y);
|
||||
const Coord qy = data.geom.DetectorToRecip(x, y + 0.5) - data.geom.DetectorToRecip(x, y - 0.5);
|
||||
return (qx % qy).Length();
|
||||
};
|
||||
auto bandwidth_radial_sq = [&](double d) -> double {
|
||||
if (bw <= 0.0 || d <= 0.0)
|
||||
return 0.0;
|
||||
const double bl = bw * lambda;
|
||||
return bl * bl / (2.0 * d * d * d * d);
|
||||
};
|
||||
|
||||
// The model works in solid-angle/polarization-corrected units (as in Run,
|
||||
// where Iobs = raw * correction). Map back to raw detector units (/ correction)
|
||||
// so the predicted image overlays directly on the original image.
|
||||
auto to_raw = [&](size_t npixel, double corrected) -> float {
|
||||
const double corr = corrections[npixel];
|
||||
return (corr > 0.0) ? static_cast<float>(corrected / corr) : 0.0f;
|
||||
};
|
||||
|
||||
// Background base layer (per-pixel azimuthal mean), full-frame pass.
|
||||
if (include_background) {
|
||||
for (size_t p = 0; p < img.size(); ++p) {
|
||||
const int bin = pixel_to_bin[p];
|
||||
if (bin >= 0 && bin < total_bin_count)
|
||||
img[p] = to_raw(p, azim_result[bin]);
|
||||
}
|
||||
}
|
||||
|
||||
double beam[2], dist_mm, detector_rot[2], rot_vec[3];
|
||||
double latt_vec0[3], latt_vec1[3], latt_vec2[3];
|
||||
BuildParameterBlocks(data, beam, dist_mm, detector_rot, rot_vec, latt_vec0, latt_vec1, latt_vec2);
|
||||
|
||||
DiffractionExperiment exp_iter = experiment;
|
||||
exp_iter.BeamX_pxl(data.geom.GetBeamX_pxl())
|
||||
.BeamY_pxl(data.geom.GetBeamY_pxl())
|
||||
.DetectorDistance_mm(data.geom.GetDetectorDistance_mm())
|
||||
.PoniRot1_rad(data.geom.GetPoniRot1_rad())
|
||||
.PoniRot2_rad(data.geom.GetPoniRot2_rad());
|
||||
|
||||
const BraggPredictionSettings settings_prediction{
|
||||
.high_res_A = experiment.GetBraggIntegrationSettings().GetDMinLimit_A(),
|
||||
.max_hkl = 100,
|
||||
.centering = data.centering
|
||||
};
|
||||
const int nrefl = prediction.Calc(exp_iter, data.latt, settings_prediction);
|
||||
const auto &predicted = prediction.GetReflections();
|
||||
|
||||
for (int ri = 0; ri < nrefl; ++ri) {
|
||||
const auto &refl = predicted[ri];
|
||||
const auto it = reference_data.find(hkl_key_generator(refl));
|
||||
if (it == reference_data.end())
|
||||
continue;
|
||||
|
||||
const double Itrue = it->second;
|
||||
const double R_bw_sq = bandwidth_radial_sq(refl.d);
|
||||
|
||||
const int min_y = std::max<int>(refl.predicted_y - radius, 0);
|
||||
const int max_y = std::min<int>(refl.predicted_y + radius, ypixel - 1);
|
||||
const int min_x = std::max<int>(refl.predicted_x - radius, 0);
|
||||
const int max_x = std::min<int>(refl.predicted_x + radius, xpixel - 1);
|
||||
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
for (int x = min_x; x <= max_x; ++x) {
|
||||
const size_t npixel = xpixel * y + x;
|
||||
|
||||
// Pure Bragg signal: Ibkg = 0 so Model() returns signal only; the
|
||||
// background is already laid down above. Same code path as Run.
|
||||
PixelObs obs{
|
||||
.x = static_cast<double>(x),
|
||||
.y = static_cast<double>(y),
|
||||
.Iobs = 0.0,
|
||||
.Ibkg = 0.0,
|
||||
.weight = 1.0,
|
||||
.A_recip = recip_area(x, y),
|
||||
.angle_rad = angle_rad
|
||||
};
|
||||
PixelResidual pr(obs, Itrue, lambda, pixel_size,
|
||||
refl.h, refl.k, refl.l, R_bw_sq, data.crystal_system);
|
||||
|
||||
double signal = 0.0;
|
||||
if (pr.Model(beam, &dist_mm, detector_rot, rot_vec,
|
||||
latt_vec0, latt_vec1, latt_vec2,
|
||||
&data.scale_factor, &data.B_factor, data.R, signal))
|
||||
img[npixel] += to_raw(npixel, signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
// Explicit instantiations for the supported (uncompressed) image pixel types.
|
||||
template void PixelRefine::Run<int8_t>(const int8_t *, const AzimuthalIntegrationProfile &, BraggPrediction &, PixelRefineData &);
|
||||
template void PixelRefine::Run<int16_t>(const int16_t *, const AzimuthalIntegrationProfile &, BraggPrediction &, PixelRefineData &);
|
||||
|
||||
Reference in New Issue
Block a user