Controller output features and integration fixes#112
Conversation
Squashed diff for paths: - python/SIREN_Controller.py - projects/injection/private/Weighter.cxx - projects/injection/public/SIREN/injection/Weighter.h - projects/injection/private/pybindings/injection.cxx - cmake/Packages/CFITSIO.cmake - vendor/googletest Source commits on dev/HNL_DIS_clean that contributed: 088b1cd (Nicholas Kamp): move around the HNL DIS fits files 0a9c9aa (Nicholas Kamp): ability to save interaction probabilities for each process 0c7ad93 (Nicholas Kamp): support sphere volume distributions in python interface 197bf14 (Nicholas Kamp): fixing build errors from the merge 1d68f30 (Nicholas Kamp): fix runtime errors on HNL generation related to particle types 4f113a1 (Nicholas Kamp): add survival probability function 521695f (Nicholas Kamp): fix issues with 2D tabular integral calculation, sampling errors with very short decay lengths, DarkNews errors, and kinematic erros in Dipole portal HNL DIS 631c12a (Nicholas Kamp): add ability to save interaction parameters to output file 82bcb80 (Nicholas Kamp): fix error in loading mat/det files ac0ce24 (Nicholas Kamp): changes to facilitate hadronic and W/Z HNL decays cf24f42 (Nicholas Kamp): add secondary fid vol flag in controller da5f6b2 (Nicholas Kamp): detector changes, functionality to save initial position of interaction_record, and fix to HNLDISFromSpline.cxx f183191 (Nicholas Kamp): more complex logic for adding darknews interactions, fix some errors in SIREN_DarkNews
542e789 to
ad12640
Compare
008a996 to
7e97b68
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds controller-side output toggles and new weighter APIs to expose interaction/survival probabilities, alongside integration/build fixes for model-file handling and CFITSIO discovery.
Changes:
- Extend
SIREN_Controllerto support explicit detector/material model paths and to optionally save interaction probabilities/parameters/survival probabilities. - Add
WeighterC++ APIs (and pybindings) to retrieve per-interaction probabilities and survival probabilities. - Improve CFITSIO CMake discovery by searching
CMAKE_PREFIX_PATH.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| python/SIREN_Controller.py | Adds new controller options for model files, fiducial restrictions for secondaries, and richer event output fields. |
| projects/injection/public/SIREN/injection/Weighter.h | Declares new probability accessors on ProcessWeighter/Weighter. |
| projects/injection/private/Weighter.cxx | Implements per-interaction probability and survival probability extraction. |
| projects/injection/private/pybindings/injection.cxx | Exposes the new Weighter APIs to Python via pybind11. |
| cmake/Packages/CFITSIO.cmake | Extends CFITSIO search paths to include CMAKE_PREFIX_PATH. |
| vendor/googletest | Vendored dependency updated (not shown in diff excerpt). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| print("Must provide either an experiment name or both a detector model file and materials model file. Exiting") | ||
| exit(0) |
There was a problem hiding this comment.
exit(0) reports success even though initialization failed, which can silently mask errors in calling code and CI. Prefer raising a Python exception (e.g., ValueError) for library code; if this is truly CLI-only behavior, use a non-zero exit code (e.g., sys.exit(1)).
| print("Must provide either an experiment name or both a detector model file and materials model file. Exiting") | |
| exit(0) | |
| raise ValueError("Must provide either an experiment name or both a detector model file and materials model file.") |
| if self.primary_physical_process.primary_type == _dataclasses.Particle.ParticleType.unknown: | ||
| self.primary_physical_process.primary_type = primary_interaction_collection.GetPrimaryType() | ||
| else: | ||
| assert(self.primary_injection_process.primary_type == primary_interaction_collection.GetPrimaryType()) |
There was a problem hiding this comment.
The assertion in the physical branch compares against self.primary_injection_process.primary_type rather than self.primary_physical_process.primary_type. This can incorrectly pass/fail depending on prior injection configuration and can hide a mismatch in the physical process type. The assert should check the physical process’ primary_type.
| assert(self.primary_injection_process.primary_type == primary_interaction_collection.GetPrimaryType()) | |
| assert(self.primary_physical_process.primary_type == primary_interaction_collection.GetPrimaryType()) |
| for secondary_injection_process in self.secondary_injection_processes: | ||
| if secondary_injection_process.primary_type == secondary_type: | ||
| inj_sec_defined = True | ||
| for secondary_physical_process in self.secondary_physical_processes: | ||
| if secondary_physical_process.primary_type == secondary_type: | ||
| phys_sec_defined = True | ||
|
|
||
| secondary_injection_process = _injection.SecondaryInjectionProcess() | ||
| secondary_physical_process = _injection.PhysicalProcess() | ||
| secondary_injection_process.primary_type = secondary_type | ||
| secondary_physical_process.primary_type = secondary_type |
There was a problem hiding this comment.
When a secondary process for secondary_type already exists, this code still creates new secondary_injection_process/secondary_physical_process instances, adds distributions to those new instances, and then discards them (because they aren’t appended). That means existing processes won’t receive the intended distributions/updates. Instead, locate and reuse the existing process objects when *_sec_defined is true; only create new ones when missing.
| if not inj_sec_defined: self.secondary_injection_processes.append(secondary_injection_process) | ||
| if not phys_sec_defined: self.secondary_physical_processes.append(secondary_physical_process) |
There was a problem hiding this comment.
When a secondary process for secondary_type already exists, this code still creates new secondary_injection_process/secondary_physical_process instances, adds distributions to those new instances, and then discards them (because they aren’t appended). That means existing processes won’t receive the intended distributions/updates. Instead, locate and reuse the existing process objects when *_sec_defined is true; only create new ones when missing.
| if type(geo)==_geometry.Cylinder: | ||
| cylinder = _geometry.Cylinder(det_placement,geo.Radius,geo.InnerRadius,geo.Z) | ||
| return _distributions.CylinderVolumePositionDistribution(cylinder) | ||
| elif type(geo)==_geometry.Sphere: | ||
| sphere = _geometry.Sphere(det_placement,geo.Radius,geo.InnerRadius) | ||
| return _distributions.SphereVolumePositionDistribution(sphere) | ||
| else: | ||
| print("Geometry type %s not supported for position distribution. Exiting"%str(type(geo))) | ||
| exit(0) |
There was a problem hiding this comment.
Using type(x) == SomeType prevents subclasses/proxies from being recognized and is generally less robust than isinstance. Also, exit(0) again signals success on an error path; prefer raising NotImplementedError/ValueError so callers can handle unsupported geometry types programmatically.
| std::tuple<siren::math::Vector3D, siren::math::Vector3D> bounds; | ||
| if(datum->depth() == 0) { | ||
| std::get<0>(bounds) = datum->record.primary_initial_position; // start location | ||
| std::get<1>(bounds) = std::get<0>(injectors[i_inj]->PrimaryInjectionBounds(datum->record)); // start of injection bounds |
There was a problem hiding this comment.
i_inj is used to index injectors / *_weighters / maps without any bounds checking, which can cause undefined behavior (crash) if Python passes an invalid injector index. Additionally, catching std::out_of_range and returning {} after printing to stdout makes failures silent for Python callers. Validate i_inj up front and raise an exception (so pybind can translate it), or at minimum return an error in a way that the Python layer can detect reliably.
| survival_probs.push_back(1 - secondary_process_weighter_maps[i_inj].at(datum->record.signature.primary_type)->InteractionProbability(bounds, datum->record)); | ||
| } catch(const std::out_of_range& oor) { | ||
| std::cout << "Out of Range error: " << oor.what() << '\n'; | ||
| return {}; | ||
| } |
There was a problem hiding this comment.
i_inj is used to index injectors / *_weighters / maps without any bounds checking, which can cause undefined behavior (crash) if Python passes an invalid injector index. Additionally, catching std::out_of_range and returning {} after printing to stdout makes failures silent for Python callers. Validate i_inj up front and raise an exception (so pybind can translate it), or at minimum return an error in a way that the Python layer can detect reliably.
| for secondary_type, decay_list in secondary_decays.items(): | ||
| # Define a sedcondary injection distribution | ||
|
|
||
| # Define a sedcondary injection distribution if necessary |
There was a problem hiding this comment.
Correct typo in comment: 'sedcondary' → 'secondary'.
| # Define a sedcondary injection distribution if necessary | |
| # Define a secondary injection distribution if necessary |
| record = _dataclasses.InteractionRecord() | ||
| record.signature.primary_type = sec_inj.primary_type | ||
| found_collection = False | ||
| # Loop through possible seconday cross sections |
There was a problem hiding this comment.
Correct typo in comment: 'seconday' → 'secondary'.
| # Loop through possible seconday cross sections | |
| # Loop through possible secondary cross sections |
| // Returns the vector of interaction physical probabilities for each process | ||
| // Since we are concerned only with the physical probability, we use the first injector since physical processes are the same for all injectors | ||
| // HOWEVER the injection bounds will change based on the injector | ||
| // so, we allow the user to specify which injector they are interseted in |
There was a problem hiding this comment.
Correct typo in comment: 'interseted' → 'interested'.
| // so, we allow the user to specify which injector they are interseted in | |
| // so, we allow the user to specify which injector they are interested in |
ad12640 to
c6019e8
Compare
7e97b68 to
7166c83
Compare
c6019e8 to
d6563e3
Compare
7166c83 to
10542f0
Compare
d6563e3 to
fd91564
Compare
10542f0 to
75b41d9
Compare
fd91564 to
00db822
Compare
75b41d9 to
d724a51
Compare
|
Closing to reopen from the commit author's account so they can be added as a reviewer. Branch unchanged; will be re-linked from the new PR shortly. |
Stack: PR 14 of 14
This PR is part of a 14-PR stack decomposing
dev/HNL_DISinto reviewable chunks.feat/hnl-3body-samplingfeat/controller-outputMerge order:
feat/hnl-3body-samplingshould land before this PR.Squashed contents
Squashed diff for paths:
Source commits on dev/HNL_DIS_clean that contributed:
088b1cd (Nicholas Kamp): move around the HNL DIS fits files
0a9c9aa (Nicholas Kamp): ability to save interaction probabilities for each process
0c7ad93 (Nicholas Kamp): support sphere volume distributions in python interface
197bf14 (Nicholas Kamp): fixing build errors from the merge
1d68f30 (Nicholas Kamp): fix runtime errors on HNL generation related to particle types
4f113a1 (Nicholas Kamp): add survival probability function
521695f (Nicholas Kamp): fix issues with 2D tabular integral calculation, sampling errors with very short decay lengths, DarkNews errors, and kinematic erros in Dipole portal HNL DIS
631c12a (Nicholas Kamp): add ability to save interaction parameters to output file
82bcb80 (Nicholas Kamp): fix error in loading mat/det files
ac0ce24 (Nicholas Kamp): changes to facilitate hadronic and W/Z HNL decays
cf24f42 (Nicholas Kamp): add secondary fid vol flag in controller
da5f6b2 (Nicholas Kamp): detector changes, functionality to save initial position of interaction_record, and fix to HNLDISFromSpline.cxx
f183191 (Nicholas Kamp): more complex logic for adding darknews interactions, fix some errors in SIREN_DarkNews
Notes
dev/HNL_DIS_clean..fitsdata files have been removed from the branch and are packaged separately.