diff --git a/include/SimCore/ReSimulator.h b/include/SimCore/ReSimulator.h index 609bbf8..7ecf2b4 100644 --- a/include/SimCore/ReSimulator.h +++ b/include/SimCore/ReSimulator.h @@ -10,14 +10,14 @@ class ReSimulator : public SimulatorBase { public: ReSimulator(const std::string& name, framework::Process& process) : SimulatorBase{name, process} {} - /* + /** * Callback for the processor to configure itself from the given set * of parameters. * * @param parameters ParameterSet for configuration. */ void configure(framework::config::Parameters& parameters) final override; - /* + /** * Run resimulation if the event is part of the requested sets of events to * resimulate * @@ -26,26 +26,41 @@ class ReSimulator : public SimulatorBase { void produce(framework::Event& event) override; private: - /* - * List of event numbers in the input files that should be resimulated if + /** + * Check if an event should be skipped during resimulation + * + * @param[in] event handle to the current event being processed + */ + bool skip(framework::Event& event) const; + + /** + * List of events in the input files that should be resimulated if * `resimulate_all_events` is false. * - * @note: If an event number in `events_to_resimulate_` is not part of the + * Each event is identified uniquely by its run number and event number. + * + * @note: If an event in `events_to_resimulate_` is not part of the * input file, it will be ignored. */ - std::vector events_to_resimulate_; + std::vector> events_to_resimulate_; /** * Whether to resimulate all events in the input files */ - bool resimulate_all_events; + bool resimulate_all_events_; + + /** + * Whether or not we should check the run number when seeing + * if a specific event should be resimulated + */ + bool care_about_run_; /* * How many events have already been resimulated. This determines the event * number in the output file, since more than one input file can be used. * */ - int events_resimulated = 0; + int events_resimulated_ = 0; }; } // namespace simcore diff --git a/python/simulator.py.in b/python/simulator.py.in index 4f1ebc2..40b4377 100644 --- a/python/simulator.py.in +++ b/python/simulator.py.in @@ -6,6 +6,25 @@ with several helpful member functions. from @PYTHON_PACKAGE_NAME@.Framework.ldmxcfg import Producer +class _EventToReSim: + """A class to hold the information identifying a specific event we wish to re-simulate + + This is an internal class used by simulator.resimulate in order to pass the event + identification to the ReSimulator class + + Attributes + ---------- + run: int + run number of the event to re-sim, -1 if we don't care about the run + event: int + event number to re-sim, required + """ + + def __init__(self, event, run = -1): + self.event = event + self.run = run + + class simulator(Producer): """A instance of the simulation configuration @@ -135,7 +154,7 @@ class simulator(Producer): sds.ScoringPlaneSD.magnet() ]) - def resimulate(self, which_events = None): + def resimulate(self, which_events = None, which_runs = None): """Create a resimulator based on the simulator configuration. This is intended to ensure that a resimulator has the same configuration @@ -147,7 +166,7 @@ class simulator(Producer): Parameters ---------- - which_events : list of event numbers, optional + which_events : list of event numbers, optional Which events from the input files to resimulate. If None, resimulate all events. @@ -155,19 +174,46 @@ class simulator(Producer): ignored. For multiple input files, if an event number is present within more - than one input file all versions will be resimulated. + than one input file all versions will be resimulated unless the which_runs + parameters is used to distinguish them. + + which_runs : list of run numbers, optional + Which runs from the input files to resimulate, ignored if no + events are listed. Runs not present in the input files will be + ignored. + + If not provided, all runs will be resimulated (i.e. the run number + check is ignored). If only one value is provided, all events requested + are also required to have that value for their run number to be resimulated. + If more than one value is provided, it must be the same length as the + number of events requested so that the event/run number pair can be required. """ resimulator = self resimulator.className = 'simcore::ReSimulator' if which_events is None: resimulator.resimulate_all_events = True + resimulator.care_about_run = False resimulator.events_to_resimulate = [ ] elif isinstance(which_events, list): resimulator.resimulate_all_events = False - resimulator.events_to_resimulate = which_events if len(which_events) == 0: raise ValueError('which_events must contain at least one element if provided') + if which_runs is None: + resimulator.care_about_run = False + resimulator.events_to_resimulate = [ _EventToReSim(event) for event in which_events ] + elif isinstance(which_runs, int): + resimulator.care_about_run = True + resimulator.events_to_resimulate = [ _EventToReSim(event, which_runs) for event in which_events ] + elif isinstance(which_runs, list): + if len(which_runs) == 0: + raise ValueError('which_runs must have at least one value if provided as a list') + if len(which_runs) != len(which_events): + raise ValueError('which_runs must have the same number of entries as which_events if more than one run is provided') + resimulator.care_about_run = True + resimulator.runs_to_resimulate = [ _EventToReSim(event, run) for event, run in zip(which_events, which_runs) ] + else: + raise ValueError('which_runs must be an int or a list of ints if provided') else: raise ValueError('which_events must be a list if provided') return resimulator diff --git a/src/SimCore/ReSimulator.cxx b/src/SimCore/ReSimulator.cxx index e985742..e31ec59 100644 --- a/src/SimCore/ReSimulator.cxx +++ b/src/SimCore/ReSimulator.cxx @@ -4,36 +4,39 @@ namespace simcore { void ReSimulator::configure(framework::config::Parameters& parameters) { SimulatorBase::configure(parameters); - resimulate_all_events = + resimulate_all_events_ = parameters.getParameter("resimulate_all_events"); - if (!resimulate_all_events) { - events_to_resimulate_ = - parameters.getParameter>("events_to_resimulate", {}); - if (events_to_resimulate_.size() == 0) { + if (!resimulate_all_events_) { + care_about_run_ = parameters.getParameter("care_about_run"); + auto configured_events{ + parameters.getParameter>( + "events_to_resimulate", {})}; + if (configured_events.size() == 0) { EXCEPTION_RAISE( "ReSimNoEvents", "ReSim was configured with resimulate_all_events marked false but " "no event numbers were requested.\n\nDid you forget to configure " "the events_to_resimulate parameter?\n"); } + for (const auto& run_event : configured_events) { + events_to_resimulate_.emplace_back(run_event.getParameter("run"), + run_event.getParameter("event")); + } } } + void ReSimulator::produce(framework::Event& event) { /* numEventsBegan_++; */ auto& eventHeader{event.getEventHeader()}; const auto eventNumber{eventHeader.getEventNumber()}; - if (!resimulate_all_events) { - auto found = std::find(std::begin(events_to_resimulate_), - std::end(events_to_resimulate_), eventNumber); - if (found == std::end(events_to_resimulate_)) { - if (verbosity_ > 1) { - std::cout << "Skipping event: " << eventNumber - << " since it wasn't part of the requested events..." - << std::endl; - } - this->abortEvent(); // get out of processors loop - return; + if (skip(event)) { + if (verbosity_ > 1) { + std::cout << "Skipping event: " << eventNumber + << " since it wasn't part of the requested events..." + << std::endl; } + this->abortEvent(); // get out of processors loop + return; } if (verbosity_ > 0) { std::cout << "Resimulating " << eventNumber << std::endl; @@ -56,7 +59,7 @@ void ReSimulator::produce(framework::Event& event) { std::to_string(eventNumber)); } - eventHeader.setEventNumber(++events_resimulated); + eventHeader.setEventNumber(++events_resimulated_); updateEventHeader(eventHeader); saveTracks(event); @@ -65,5 +68,28 @@ void ReSimulator::produce(framework::Event& event) { runManager_->TerminateOneEvent(); } +bool ReSimulator::skip(framework::Event& event) const { + /** + * If we are configured to simply resimulate all events, this + * function always returns false. + */ + if (resimulate_all_events_) return false; + /** + * Otherwise, we check the event number + * (and also its run number if we care_about_run_) + * against the list of run/event pairs that we are + * interested in re-simulating. + */ + auto found_event_to_resim = std::find_if( + std::begin(events_to_resimulate_), std::end(events_to_resimulate_), + [&](const std::pair& run_event) -> bool { + bool runs_match = true; + if (care_about_run_) + runs_match = (event.getEventHeader().getRun() == run_event.first); + return event.getEventNumber() == run_event.second and runs_match; + }); + return (found_event_to_resim == std::end(events_to_resimulate_)); +} + } // namespace simcore DECLARE_PRODUCER_NS(simcore, ReSimulator)