diff --git a/python/pyMaCh3.cpp b/python/pyMaCh3.cpp index c88a6ead..6773be9b 100644 --- a/python/pyMaCh3.cpp +++ b/python/pyMaCh3.cpp @@ -1,6 +1,9 @@ #include +#include #include "python/pyMaCh3.h" +#include // needed for std::vector <-> python list conversion + #include "Samples/SampleHandlerAtm.h" #include "Samples/SampleHandlerBeamFD.h" #include "Samples/SampleHandlerBeamND.h" @@ -136,6 +139,94 @@ class MaCh3DunePyBinder : public MaCh3PyBinder { }) ); + + // #################################################### + // Factory function: GetMaCh3DuneInstance + // #################################################### + m_samples.def("GetMaCh3DuneInstance", + [](const std::string& SampleType, + const std::string& SampleConfig, + ParameterHandlerGeneric* param_handler, + OscillationHandler* BeamOscillator_, + OscillationHandler* AtmOscillator_, + BeamNDCov beamNDCov) -> SampleHandlerBase* { + + // GetMaCh3DuneInstance takes std::unique_ptr&, + // so wrap the raw (Python-owned) pointer in a real unique_ptr, call + // the function, then release it so we don't double-delete the + // object when this local goes out of scope. + std::unique_ptr param_handler_ptr(param_handler); + + std::shared_ptr beam_osc_ptr; + if (BeamOscillator_ != nullptr) { + beam_osc_ptr = std::shared_ptr(BeamOscillator_, [](OscillationHandler*){}); + } + + std::shared_ptr atm_osc_ptr; + if (AtmOscillator_ != nullptr) { + atm_osc_ptr = std::shared_ptr(AtmOscillator_, [](OscillationHandler*){}); + } + + SampleHandlerBase* Sample; + try { + Sample = GetMaCh3DuneInstance( + SampleType, SampleConfig, param_handler_ptr, + beam_osc_ptr, atm_osc_ptr, beamNDCov); + } catch (...) { + param_handler_ptr.release(); // ownership stays with the caller/Python side + throw; + } + + param_handler_ptr.release(); // ownership stays with the caller/Python side + + return Sample; + }, + "Create a MaCh3 DUNE SampleHandler instance based on SampleType " + "(one of \"BeamFD\", \"BeamND\", \"Atm\", \"BeamNDGAr\")", + py::arg("SampleType"), + py::arg("SampleConfig"), + py::arg("param_handler"), + py::arg("BeamOscillator") = nullptr, + py::arg("AtmOscillator") = nullptr, + py::arg("beamNDCov") = BeamNDCov(), + py::return_value_policy::take_ownership + ); + + // #################################################### + // Factory function: MaCh3DuneSampleFactory + // #################################################### + m_samples.def("MaCh3DuneSampleFactory", + [](Manager* fit_manager, ParameterHandlerGeneric* param_handler) -> std::vector { + + // Same ownership trick as GetMaCh3DuneInstance: MaCh3DuneSampleFactory + // takes std::unique_ptr& and std::unique_ptr&, + // but Python owns these objects as raw pointers. Wrap them temporarily, + // call the factory, then release before returning so Python retains + // ownership and nothing gets double-freed. + std::unique_ptr fit_manager_ptr(fit_manager); + std::unique_ptr param_handler_ptr(param_handler); + + std::vector result; + try { + result = MaCh3DuneSampleFactory(fit_manager_ptr, param_handler_ptr); + } catch (...) { + fit_manager_ptr.release(); + param_handler_ptr.release(); + throw; + } + + fit_manager_ptr.release(); + param_handler_ptr.release(); + + return result; + }, + "Build the vector of DUNE SampleHandlers described by General:DUNESamples " + "in the fit Manager config, using the given parameter handler for systematics " + "and oscillation parameters", + py::arg("fit_manager"), + py::arg("param_handler"), + py::return_value_policy::take_ownership + ); } };