STM diffusion model resampler updates, plumbing, workflows, and tools - #1960
STM diffusion model resampler updates, plumbing, workflows, and tools#1960YongyiBWu wants to merge 114 commits into
Conversation
Register a new plugin and add a training module that reads training samples from a ROOT TTree instead of art::Event collections. Adds CMakeLists entry for VDResamplerTrainFromRoot and a new source file implementing an EDAnalyzer that: parses fhicl configuration (including tree/file, two-stage vs all-at-once options, optimizer/noise schedule, training hyperparameters, and SaveEpochs), opens a ROOT TTree, transforms entries via VDResampler transforms into DiffusionTrainingSample objects, trains ScoreBasedDiffusionModel(s) for the configured number of epochs, and saves model files at requested epochs and the final epoch. Intended as a ROOT-file-driven counterpart to VDResamplerTrain to enable model checkpointing and configurable training from external TTrees.
… dominating the training, added gradient clipping monitor
Introduce adaptive per-dimension gradient weighting and an EMA copy of network parameters for inference. Added new constructor options (useDimWeightController, dimWeightEMADecay, useEMANetwork, emaNetworkDecay) and internal state (dimLossEMA_, dimWeights_, emaNetwork_). Training accumulates per-dimension MSE, updates an EMA to compute normalized dimWeights_, and applies those weights to per-dimension gradients; EMA network is updated after optimizer steps. Added forwardInference() for const inference passes (used when generating samples) and updateEMANetwork() to step the EMA. Serialization and loadModel were extended to save/restore dim-weight controller state and EMA network weights with backwards-compatible fallbacks. Also exposed getDimWeights() and logging to reflect the new options.
Introduce an optional sinusoidal time embedding for diffusion time t (timeEmbeddingDim) while preserving backward compatibility (0 = raw scalar). Added timeEmbeddingDim_ member, timeEmbed(t) helper, validation (must be 0 or even >=2), and integrated the embedding into all places where t was previously appended to network inputs (training, inference, sampling/Euler/RK steps). Updated constructor signature/defaults, network input size calculation and consistency checks, model serialization/deserialization to include timeEmbeddingDim, and logging. This enables richer time encodings via pairs [sin(2π·2^i·t), cos(2π·2^i·t)] while keeping existing behavior by default.
Add a new header STMMC/inc/VDResamplerTrainCommon.hh that centralizes shared TrainState, ModelBuildParams, curriculum/geometry validation, normalization (Welford), sample collection, model construction and the training loop for VDResampler training. Refactor VDResamplerTrainFromRoot_module.cc to use the new common header and TrainState, simplify fhicl config population, RNG member names, and remove large amounts of duplicated training/setup code. This prepares common logic for reuse by VDResamplerTrain and VDResamplerTrainFromRoot and improves maintainability of model/checkpoint handling, curriculum management, and data normalization.
Extend ScoreBasedDiffusionModel with per-coordinate Fourier (sin/cos) input embeddings for state and condition vectors, exposing inputEmbeddingDim and conditionEmbeddingDim members and wiring them through constructor, save/load (binary version bumped to 2) and network input assembly (buildNetworkInput). Add optional t-focus sampling (tFocusLow/tFocusHigh/tFocusFraction) to bias training samples towards a target t window, with validation and curriculum support; sampling logic updated to draw from the focus window with given probability. Implement a one-step denoising diagnostic (denoiseOneStep) and plumbing to run a denoising diagnostic that writes ROOT TTrees instead of training when configured (VTResamplerTrain* modules: new fhicl options and TrainState fields, runDenoiseDiagnostic helper). Update training, sampling, CSV/text IO, and messages to account for new embeddings and focus-window parameters, and add various input checks and informative logging.
Introduce a shared reverse integrator (reverseDiffuseFrom) in the ScoreBasedDiffusionModel and refactor generateSample to call it. Add partialReverseSample API that noises a normalized sample to a snapped grid time t0 and runs the full reverse sampler from that t0 down to 0. Implementations include sigma safety fix and loop start based on stepStart. Add partial-reverse diagnostic tooling to STMMC: new TrainState fields and fhicl parameters to configure t0 values, sample counts, solver choices, diffusion steps and sigma threshold. Implement runPartialReverseDiagnostic which noises training samples to each t0, runs the model.partialReverseSample, and writes truth vs sampled values to ROOT TTrees (one tree per t0). Integrate diagnostics into runTraining via a diagnosticMode / runDiagnostics helper; also propagate the denoising diagnostic EMA option. The partial-reverse diagnostic is intended to localize where the sampler loses features by scanning start times.
Improve robustness of ScoreBasedDiffusionModel::loadModel by validating all binary reads and sizing fields. Short/failed reads now throw a clear cet::exception with context instead of allowing uninitialized data to propagate; vector/matrix payload reads, network weight rows, biases, normalization arrays, and EMA sections are explicitly checked. A sanity-bound checkCount (kMaxBinElems = 1e8) prevents implausible element counts from causing huge allocations and converts corrupt/truncated checkpoints into informative errors.
Three changes: (1) Update VDResamplerTrainingPlan.fcl: reduce SBDMplannerPatience 40->25 and SBDMplannerMinEpochsPerPhase 65->50 and adjust comment to match new window+patience math. (2) Add trailing FCL overrides in VDResamplerConfigureCommon.hh to set analyzers.<module>.InputRootFile to @nil and emit empty mu2emetadata key arrays for clearer produced FCL. (3) Revise ConvergenceTracer in VDResamplerTrainCommon.hh: use a symmetric trimmed mean (kTrimFraction=0.2) for smoothing, require a full window before counting improvements or patience, and add explanatory comments to make phase-convergence detection robust to heavy-tailed per-epoch outliers.
Replace the deprecated radial basis options with V2_ATANH_SQRT, updating the parser, transform math, validation plots, and training-plan defaults. The new map uses u = atanh(sqrt(rho)) and rho = tanh^2(u), which stretches the inner core without inflating the rim range, avoiding the narrow core artifact seen after z-scoring with the broader ratio-style maps. Comments and FHiCL help strings were revised to document the rationale and the remaining supported basis choices.
Adds explicit per-phase peak importance sampling toggles to the VD resampler training plan and uses them to disable peak sampling by default. One photon 1809 keV training leaf switches to late-phase peak sampling over a narrow time window to improve coverage of the prompt timing peak without changing the core curriculum schedule.
Introduce a new radial position basis V3_AtanhSq (u = atanh(rho^2)) and wire it through the codebase. Changes: add enum entry and name mapping, implement forward/back transforms and math comments (VDResamplerTransforms.hh); accept "V3_ATANH_SQ" in parsePositionBasis (VDResamplerTrainCommon.hh); document the new option in module fhicl comments (VDResamplerTrain_module.cc, VDResamplerTrainFromRoot_module.cc); update validation plot axis top text and utop handling to account for the new basis and increase one correlation plot p-axis upper bound from 500 to 1000 (VDResamplerValidationPlots.hh). The new basis is intended for species with mass concentrated at large rho (compresses the centre and spends u-range further out).
Add px, py, pz double variables and TTree branches to VirtualDetectorTree_module.cc and populate them from step.momentum().x/y/z in analyze(). This records particle momentum (MeV) per step alongside position and energy for diagnostic/analysis use; no other behavior changes.
Remove global SBDMpositionBasis settings and specify SBDMpositionBasis per particle in VDResamplerTrainingPlan.fcl to tune position mapping for each sample (adds V1_ATANH, V3_ATANH_SQ, V2_ATANH_SQRT, etc., and adjusts a few stage-table selections). In STMMC/inc/VDResamplerValidationPlots.hh add #include <TLine>, draw the ratio axis first, and draw a dashed unity reference line (styled and deletable) before drawing the ratio points so deviations are visually clear. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge diffusion model updates
Add checks in HPGeTree and LaBrTree to avoid dereferencing non-dereferenceable SimParticle Ptrs. Introduce nNoParent counter and return when parent.isNull() or !parent.isAvailable() to prevent ProductNotFound from compressed SimParticleCollections. Update parent-walking loop to verify parent->parent().isNonnull() and isAvailable() before dereference. Log the number of steps whose SimParticle had no reachable parent so large truncated genealogies are visible. Small, defensive change to prevent runtime errors and clarify behavior for primaries.
This reverts commit e11c7d0.
Diffusion model syncing development branch
|
Hi @YongyiBWu,
which require these tests: build. @Mu2e/fnalbuild-users, @Mu2e/write have access to CI actions on main. ⌛ The following tests have been triggered for 5bb835f: build (Build queue - API unavailable) |
|
☀️ The build tests passed at 5bb835f.
N.B. These results were obtained from a build of this Pull Request at 5bb835f after being merged into the base branch at 0ef02a6. For more information, please check the job page here. |
|
Too much to check in any detail. I do have one high-level question, perhaps for @oksuzian or @brownd1978 . So far, I think, we've kept ML training out of the Offline library. I think the idea is generally if it doesn't run in sim or reco, we shouldn't build it here, since everyone builds it all every time. I think MLTrain repo was created along these lines, and maybe some other training is in personal repos. I think the trend to the future is "modularize" more than "gather". |
|
I want to clarify on locating this repo here rather than in the MLTrain . As a resampler, both input and output of this code is in the sim workflow, and this module is important to run any STM simulations. The model implementation here is fully customized (C++) and depend on no external packages. Outputs are in customized binary files. So in a sense it can be considered as a sim production using ML method. Of course, if you want this to be moved elsewhere, please let me know and we can discuss. |
oksuzian
left a comment
There was a problem hiding this comment.
PR Review Summary — #1960
Reviewed at head 5bb835f. First pass; no prior reviews on the PR.
Decision
- 🟡 comment only
Scope understood
- A score-based diffusion model resampler for the STM:
MachineLearningTools/ScoreBasedDiffusionModelgains SDE/ODE samplers, EMA networks and a versioned checkpoint format, andSTMMCgains the configure → train → generate → validate workflow around it, plus a newLaBrTreeanalyzer. - The PR body's claim that scope is limited to STMMC and the DM toolkit holds:
ScoreBasedDiffusionModelhas exactly four consumers, all insideSTMMCand all touched here, and nothing else in Offline includesMachineLearningTools. Nothing outside this workflow can regress at merge, which is why the findings below are a comment rather than a change request. Production#576supplies the job fcl and needs this to merge first.
Findings
-
🟠 [S1] A training run that never converges writes the model file the generate step reads, and the job exits 0
- Evidence:
VDResamplerTrainCommon.hh:2137-2147logs anmf::LogWarning, then callsmodel.saveModel(outFile, basisTag)and returns.outFileiss.stage1ModelFile/s.stage2ModelFile/s.allAtOnceModelFile— the exact paths the generator resolves.runTrainingreturnsvoidand is called fromendJob(VDResamplerTrain_module.cc:359,VDResamplerTrainFromRoot_module.cc:360), so art reports success. The checkpoint recordsbasisTagbut nothing about convergence. - The message says "Stopping training", and that is not what happens.
trainLoopis a lambda declared at:1837and called three times (:2190stage 1,:2221stage 2,:2240all-at-once); returning from it drops control back intorunTraining, which proceeds to theif (s.trainStage2)block and trains stage 2 against a stage-1 model that never converged. - Impact: this is the "a warning is not error handling" case, and the file's own
PositionBasiscomment block records that it has already happened once — runs "were later found to have stopped with a training loss still above 0.99, i.e. undertrained" after being mistaken for a coordinate bug. OneLogWarningamong thousands of per-epochLogInfolines in a grid log is not a failure signal. - Suggested fix: throw, or write the non-converged state to
<base>.notConverged.datand not tooutFile, so the downstream step fails on a missing input rather than succeeding on a bad one. Either way, correct the "Stopping training" wording or make it true.
- Evidence:
-
🟠 [S1]
VDResamplerGenerateMixtakes the VD geometry from its own fcl defaults, contradicting the invariant the PR states elsewhere- Evidence:
VDResamplerConfigure_module.cc:74-76says "VirtualDetectorID (and VDz0/VDr) are NOT module parameters: they come from the training plan'scommon_training_config, the single source of truth", andVDResamplerRegeneratePlots_module.cc:273-277honours that, readingcommon.get<double>("VDr")andcommon.get<double>("VDz0").VDResamplerGenerateMix_module.cc:197-206instead declares them asfhicl::Atom<double>with hardcoded defaults 37700.39 and 2000.0, and cross-checks only the VD id, against the summary file name (:626). - Impact:
GenerateMixis the one module that emits physics.VDris baked intoinvertPositionandinvertMomentumV2FromPtotthroughInverseParams(:856), so a campaign trained with a differentVDrand generated from a fcl that omits the key produces every position and every momentum slope wrong by that ratio, with rc=0 and no warning. - Suggested fix: give
GenerateMixthe samecommon_training_configreadRegeneratePlotshas — it already loadstrainingPlanFile_for the peak tags at:374-378— and drop the three atoms. PersistingVDr/VDz0in the checkpoint next tobasisTagand throwing on disagreement would be stronger still.
- Evidence:
-
🟠 [S1]
loadPDGIdFromFileNamesearches the whole path, so a directory name can decide which particle is generated- Evidence:
VDResamplerGenerateFromModel_module.cc:64isconst size_t pdgPos = fileName.find("pdg");with no basename strip. The result feedspdt_->particle(pdgId_).mass()(:498,:532),PDGCode::type(pdgId_)(:540) and the resampler's source filter (:411), and is never cross-checked against the checkpoint. - Impact: a model at
.../train_pdg22/nts.mu2e.STMVDResamplerModel_VD116_pdg2112_stage2....datyieldspdgId_ = 22. The job then emits photons, with photon masses, drawn from a neutron model, and selects photons out of the mother file. Grid output areas are commonly named after the training job, so this is not a contrived path. - Suggested fix: strip the directory before the search —
fileName.substr(fileName.find_last_of("/\\") + 1)— and, better, storepdgIdin the checkpoint alongsidebasisTagand verify it.
- Evidence:
-
🟠 [S1] Nine
SetBranchAddressreturn values are ignored over uninitialized locals- Evidence:
VDResamplerTrainFromRoot_module.cc:314-325declaresdouble time, x, y, z, px, py, pz; int stepPdgId; ULong64_t vdId;and binds each with an uncheckedttree->SetBranchAddress(...). The same pattern is inVDResamplerPtotResampler.hh:75-86. CI agrees independently: clang-tidy on this head reportsvariable 'time' is not initializedand eight siblings at exactlyVDResamplerTrainFromRoot_module.cc:314and:316. - Impact:
SetBranchAddressreturnskMissingBranchand binds nothing when a branch is absent, soGetEntry(i)leaves the locals as stack garbage and the selection at:329compares it. Point either module at a dump written by an older Configure, or atVirtualDetectorTreeoutput, and the model trains on unrelated numbers. It is also formally undefined behaviour. - Suggested fix: check each return and throw naming the missing branch, and initialize the locals at declaration.
- Evidence:
-
🟠 [S1] Every generated training fcl ends with an unconditional
InputRootFile : @nil, and on the art path that key does not exist- Evidence:
VDResamplerConfigureCommon.hh:509-511emits the resolvedInputRootFile : "<path>"inside the analyzer block when the plan sets it, and then:578unconditionally appendsphysics.analyzers.<moduleName>.InputRootFile : @nilas a trailing override that, by the comment's own reasoning at:575-577, wins. So theinputRootFileSetbranch can never survive into the emitted file. Separately,trainModuleisVDResamplerTrainwhentrainingFromROOTFileis false (:474), andVDResamplerTrain_module.cccontains noInputRootFileparameter at all — zero occurrences — while usingart::EDAnalyzer::Table<Config>(:158), which rejects unrecognised keys. - Impact: the art path is a documented, required plan option (
trainingFromROOTFileis on the required-key list at:408, and the plan has an "ART-source settings" section atVDResamplerTrainingPlan.fcl:63), and every fcl generated for it fails configuration validation. On the ROOT path the placeholder appears to be deliberate, but it silently discards a value the plan supplied. - Suggested fix: emit the trailing
@nilonly when the plan left it unset and the path is ROOT; emit the resolved value otherwise; and emit nothing on the art path.
- Evidence:
-
🟠 [S1] The axis-report loop indexes a six-element array with slot 6
- Evidence:
kRhoTransSlot = 6(VDResamplerValidationPlots.hh:281) andusing TransformedStatsBySlot = std::array<TransformedDimStats, 6>(VDResamplerGenerateCommon.hh:75), so valid indices are 0-5.rhoTransis pushed withtransformed = trueandslot = kRhoTransSlotat:428, and the loop at:751-753doesconst TransformedDimStats& st = (*stats)[s.slot];with no bound check. The comment at:280even notes the slot sits outside the array's range. - Impact: reads past the end of a caller-stack local on any job with
doValidationPlotsand model statistics — the normal path.st.validis whatever follows, so the log prints either a fabricated statistics line or the right fallback by luck; under_GLIBCXX_ASSERTIONSit aborts. The sizing loop at:416-418avoids this deliberately; the reporting loop added later did not get the same guard. - Suggested fix:
if (s.slot >= static_cast<int>(stats->size())) continue;at the top of the loop body.
- Evidence:
-
🟠 [S1] An installed Offline fcl includes a Production fcl it does not use
- Evidence:
STMMC/fcl/VDResamplerTrainingPlan.fcl:34-35includesOffline/fcl/standardServices.fclandProduction/JobConfig/pileup/STM/prolog.fcl. The file defines noprocess_name,source,servicesorphysics— it is a pure data file read throughParameterSetFromFile— and it contains zero@local::or@sequence::references, so neither include contributes anything. - Impact: the file is installed (
CMakeLists.txt:161) and is the defaulttrainingPlanFileforVDResamplerReconfigure.fcl:36andVDResamplerRegeneratePlots.fcl:55. In an Offline-only working area, with Production not onFHICL_FILE_PATH, those jobs andVDResamplerConfiguredie at construction on an unresolvable include rather than on anything about the plan. It also inverts the repo dependency: Production depends on Offline, not the reverse. - Suggested fix: delete both include lines.
- Evidence:
-
🟠 [S1] The basis tag does not cover the constants the bases are built from, and an unknown basis falls through to V1
- Evidence:
packBasisTag(VDResamplerTransforms.hh:292-298) encodes only the layout and the two basis enums. The numeric parameters those enums carry are compile-time constants whose comments invite tuning —kUrSlopeScale/kUphiSlopeScaleat:51-52("so each can be tuned to its own spread"),kTBulkCenter/kTTailScaleat:63-64("kTTailScale sets the asinh width"), andkP0at:19("tunable momentum scale"). Retune one and every previously trained model is decoded with a different map whilecheckModelLayout, the peak-tag check and the layout check all still pass. Separately,unpackPositionBasisandunpackMomentumBasis(:302-307) are uncheckedstatic_casts, and bothradialForward(:386-389) andradialInverse(:400-403) end incase V1_Atanh: default:, so an out-of-range value silently gets the V1 map. Layout is guarded (VDResamplerGenerateFromModel_module.cc:100); the two bases are not. - Impact:
kUrSlopeScalechanging from 0.05 to 0.02 makes every generated slope 2.5x too small, which propagates intopz = pTot/sqrt(1+ur²+uphi²), with no error at any level. And adding aV4enumerator later compiles clean and behaves as V1. - Suggested fix: bump the enumerator whenever a scale changes, or persist the scales in the checkpoint and compare on load; and drop the
default:arms so-Wswitchcatches a new enumerator at compile time, adding an explicit range check where the tag is unpacked.
- Evidence:
-
🟠 [S1]
forwardTransformSampleV2uses two different values ofpz, and the instrumented one is the harmless half- Evidence:
VDResamplerTransforms.hh:601callsextrapolateAndCenter(x, y, z, px, py, pz, ...)with the rawpz, which divides by it unguarded at:364. Eight lines later,:608-612floors the samepzatkPzSafetyEpsilonfor the slope division and records the fallback throughPzFallbackStats. - Impact: both training entry points cut
pz <= 0(VDResamplerTrain_module.cc:322,VDResamplerTrainFromRoot_module.cc:329), butpz = 1e-30passes. The extrapolation factor goes to ~1e30, the radius with it, andforwardPosition'srho = std::min(rho, 1.0 - kRhoClampEpsilon)at:413-415then silently relocates the hit to the detector rim — thatstd::minis doing two jobs, the documentedrho == 1guard and an undocumented out-of-detector truncation. The endJob warning meanwhile reports only the slope-division fallback.PzFallbackStatsis exactly the right pattern; it is simply attached to the wrong half. - Suggested fix: use
pzSafefor the extrapolation too, and give the radial clamp its own counter so an out-of-radius hit produces a summary warning the way thepzfallback does.
- Evidence:
-
🟡 [S2] Smaller items, each with a one-line fix
- No output stream is ever checked after writing.
saveModel(ScoreBasedDiffusionModel.cc:1703-1841) andsaveModelCsv(:1848-2052) check only that the open succeeded and never testout.good();VDResamplerConfigure_module.cc:257-315does the same for the hit summary. A grid worker hitting quota mid-write produces a truncated checkpoint or summary and exits 0. - The binary loader omits three shape checks the CSV loader performs.
numLayers(ScoreBasedDiffusionModel.cc:2207) is never compared tolayers(:2159), so the copy loops at:2345and:2373indexloadedNetwork/loadedEmaNetworkout of bounds when a file disagrees. The CSV path checks all of this. - Four modules construct
mu2e::ParameterSetFromFile(VDResamplerConfigure_module.cc:138,VDResamplerReconfigure_module.cc:107,VDResamplerGenerateMix_module.cc:376,VDResamplerRegeneratePlots_module.cc:264) but onlyVDResamplerRegeneratePlotsdeclaresOffline::GeneralUtilitiesinCMakeLists.txt. The other three link through a transitive edge, which the coding standard rules out.src/SConscript:51does declare it, so the two build systems disagree. VDResamplerConfigureCommon.hh:572-573seeds the generated fcls fromstd::time(nullptr). All of them are written in oneendJob, so every particle in a source gets the samebaseSeed— the opposite of the comment's stated intent — and re-running Configure on the same summary produces a different set of fcls, so a training result cannot be reproduced from its inputs. Deriving the seed from(runNumber, sourceIndex, pdg, VD id)gives both properties.
- ⚪ [S3] Collected
- Physics-affecting fcl parameters carry C++ defaults, which Mu2e reserves for
verbosityLevel/diagLevel:pdgIDdefaults to 22 andVirtualDetectorID/VDz0/VDrto 116/37700.39/2000.0 inVDResamplerTrain_module.cc:52-57and the same set inVDResamplerTrainFromRoot,VDResamplerGenerateFromModelandVDResamplerGenerateMix. A hand-written training fcl that omitspdgIDtrains photons and writes them under whatever model name was given. STMMC/fcl/MakeTree.fcl:43-48keeps the supersededStage2LaBrblock commented out directly above its replacement. The explanatory comment at:40-42is worth keeping; the dead block is not.LaBrTree_module.cc:56and:67carry an unusedParticleDataList.hhinclude and an unusedVolumeId_typetypedef, both inherited fromHPGeTree_module.cc.VDResamplerConfigure_module.cc:234usesmf::LogInfo log("Virtual Detector Resampler Training Configuration Summary"). A message-facility category is a fcl routing key, so one containing spaces cannot be addressed indestinations.*.categories.
Verified
- 🟢 The blast radius is genuinely confined.
ScoreBasedDiffusionModelis included by exactly four files, all inSTMMCand all in this PR; a repository-wide search forMachineLearningToolsreturns only this package,STMMC, and the two build files. Nothing outside this workflow changes behaviour at merge. - 🟢
MachineLearningTools/CMakeLists.txtneeds no edit: itsCLHEP/MF_MessageLogger/cetlib_exceptlist still covers every non-standard include in the file, and<limits>is the only include this PR adds. - 🟢 All ten
*_module.ccfiles are registered inCMakeLists.txt, including the newLaBrTree(:47),VDResamplerReconfigure(:102),VDResamplerTrainFromRoot(:118) andVDResamplerRegeneratePlots(:150);src/SConscriptglobs*_module.ccthroughhelper.make_plugins, so nothing is missing there. - 🟢 Every module uses validated FHiCL —
art::ED{Analyzer,Producer}::Table<Config>atVDResamplerConfigure:84,GenerateFromModel:225,GenerateMix:223,Reconfigure:76,RegeneratePlots:216,Train:158,TrainFromRoot:153. None is left on a bareParameterSet. - 🟢 Random numbers are handled correctly. Every engine comes from
createEngine(ServiceHandle<SeedService>()->getSeed()), and a search across all nine module files forstd::rand,srand,mt19937,TRandomandgRandomreturns nothing, so two grid jobs cannot produce identical samples. (The seed problem in finding 10 is in the generated fcl, not the engines.) - 🟢 No forbidden handle caching: no
art::Handle/ValidHandlemembers, noGeomHandle/ConditionsHandle/ProditionsHandleanywhere, products fetched per event throughProductToken. The cachedGlobalConstantsHandle<ParticleDataList>members are the permitted service-handle case. - 🟢
normalizeDatadoes guard a zero standard deviation —ScoreBasedDiffusionModel.cc:559-561throws acet::exceptionrather than dividing — so a constant coordinate cannot poison the whole training set. - 🟢 The mixing weights in
GenerateMixare normalized correctly, and disabling a source renormalizes the survivors:referencePots_is the maximum over enabled sources only (:527-546) andtotalSourceWeight_sums only enabled entries, so the joint probability is proportional to each species' per-POT yield. The absolute-rate consequence of disabling a source is stated in the fcl comment (:118-124) and quantified at endJob byreportPotEquivalence(:926-976). - 🟢
LaBrTreeis not a careless copy ofHPGeTreeon the point that matters most.HPGeTree_module.cc:180-182keepsx <= xBeamCentrefor HPGe andx >= xBeamCentrefor LaBr;LaBrTree_module.cc:193keepsx >= xBeamCentre, matching the donor's LaBr branch rather than its HPGe branch. TheDetectorparameter and its validation vector are correctly dropped. Its energy limitation is documented rather than hidden — a FIXME block at:11-27, a constructorLogWarningat:113-117, an inline FIXME at:204and an endJob reminder at:251-252. - 🟢 The
HPGeTreechange is a real fix, not a workaround. The oldwhile (std::find(..., parent->parent()->id()) ...)dereferenced an uncheckedart::Ptr;:121-124and:132-133add null and availability guards, the early return yields the same answer the next test would have, andnNoParentis reported at endJob (:240-242) rather than swallowed. - 🟢 Every VD id used is 116 or below (
VirtualDetectorTree, the plan'sVirtualDetectorID : 116, and the 88/89/90/100/101/116 list in the Production job), so none of this is exposed to theVirtualDetectorIdrenumbering atv13_36_00where 117-135 silently mislabel. - 🟢 The
triggertest's:question:/ return code 1 is not this PR's doing — it shows the same result on PRs #1955, #1957 and #1949.
Validation check
- Build/tests run: not by me. FNALbuild is green at
5bb835ffor the build and every physics job. Three informational results are worth acting on: the whitespace check reports trailing whitespace atScoreBasedDiffusionModel.cc:22and on twelve lines ofVDResamplerTrainingPlan.fcl, plusVDResamplerReconfigure.fclwith no terminal newline; clang-tidy reports 20 errors and 138 warnings, largely the uninitialized locals in finding 4; andLaBrTree_module.cccarries 3 FIXME and 1 TODO, which are the honest documentation noted above rather than leftovers. - Config contract check: partial. Every plan key was resolved against the
fhicl::Namedeclarations in both train modules, and the four-sourceversionTaglist matchesdataSourceNames()in order and length. Nofhicl-dumpwas produced and no job was run. - Cross-repo consistency: merge-order note only.
Production#576supplies the job fcl and depends onOffline/STMMC/fcl/VDResamplerTrainingPlan.fcl, which this PR adds, so this merges first. I reviewed #576 separately.
Not checked
- Nothing was built, run or profiled; every finding above is static reading.
- Whether the model actually reproduces the STM VD116 distributions. No validation output, loss curve or comparison plot is attached to the PR, and that is the evidence this change ultimately rests on.
- Whether
VDz0 = 37700.39andVDr = 2000.0are the current correct values for VD116 — I did not consult a geometry file. Findings 2 and 11 are about where the numbers live, not about the numbers being wrong. ParticleDataList::particle(int)behaviour for an unknown pdgId. If it returns a default rather than throwing, the sixpdt_->particle(pdgId).mass()call sites are silent-degradation points feeding a wrong mass intoE = sqrt(p²+m²) - m. One line for you to confirm.- The empirical claims embedded in the
PositionBasiscomment block — the measuredcoreFrac, the0/100000clamp measurement, the per-band u-range table. The analytic entries I spot-checked are right; the measured ones I cannot reproduce from source.
Author follow-ups
- Decide what a non-converged training run should do (finding 1); it is the one that can quietly put a bad model into a production campaign.
- Make
GenerateMixread the VD geometry fromcommon_training_configlike the other modules (finding 2). - Fix the two path/branch parsing bugs — the basename strip in
loadPDGIdFromFileName, and the checkedSetBranchAddressreturns over initialized locals (findings 3 and 4). - Fix the generated-fcl emission so the art path does not write a key its module lacks (finding 5), and drop the two unused includes from
VDResamplerTrainingPlan.fcl(finding 7). - Attach whatever validation output you have — a loss curve and one generated-vs-source comparison for a single species would let a reviewer judge the physics rather than only the plumbing.
|
This is a massive PR - 12,000 lines of code in a single PR. |
This PR reflects the new developments for the STM diffusion model resampler. Additional features were added for more dynamic model training. Additional bases were added for pre-training coordinate transformation. The plumbing between the DM implementation and the STM applications were completed. A full workflow was set up and some tools were added. Scope of change is strictly limited to STMMC and DM toolkit.