diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ac609638..6e7b55f8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,11 +16,10 @@ cmake_minimum_required(VERSION 3.20 FATAL_ERROR) find_package(cetmodules 3.20.00 REQUIRED) -project(sbncode VERSION 10.04.06.01 LANGUAGES CXX) +project(sbncode VERSION 10.22.00 LANGUAGES CXX) message(STATUS "\n\n ========================== ${PROJECT_NAME} ==========================") - include(CetCMakeEnv) cet_cmake_env() @@ -40,6 +39,7 @@ cet_report_compiler_flags(REPORT_THRESHOLD VERBOSE) # these are minimum required versions, not the actual product versions find_package( art REQUIRED ) +find_package( Eigen3 REQUIRED) find_package( messagefacility REQUIRED ) find_package( art_root_io REQUIRED ) find_package( nusimdata REQUIRED ) @@ -77,11 +77,13 @@ find_package( CLHEP REQUIRED ) find_package( ROOT REQUIRED ) find_package( Geant4 REQUIRED ) find_package( Boost COMPONENTS system filesystem REQUIRED ) +find_package( geant4reweight REQUIRED ) # macros for dictionary and simple_plugin include(ArtDictionary) include(ArtMake) include(BuildPlugins) +include(SBNutils) add_subdirectory(sbncode) add_subdirectory(fcl) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..dbdc04e58 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,140 @@ +{ + "configurePresets" : [ + { + "cacheVariables" : { + "CMAKE_BUILD_TYPE" : { + "type" : "STRING", + "value" : "RelWithDebInfo" + }, + "CMAKE_CXX_EXTENSIONS" : { + "type" : "BOOL", + "value" : "OFF" + }, + "CMAKE_CXX_STANDARD_REQUIRED" : { + "type" : "BOOL", + "value" : "ON" + }, + "sbncode_ADD_ARCH_DIRS_INIT" : { + "type" : "INTERNAL", + "value" : "LIBRARY_DIR;BIN_DIR" + }, + "sbncode_ADD_NOARCH_DIRS_INIT" : { + "type" : "INTERNAL", + "value" : "GDML_DIR;INCLUDE_DIR;FHICL_DIR;FW_DIR" + }, + "sbncode_BIN_DIR_INIT" : { + "type" : "STRING", + "value" : "bin" + }, + "sbncode_FHICL_DIR_INIT" : { + "type" : "STRING", + "value" : "fcl" + }, + "sbncode_FW_DIR_INIT" : { + "type" : "STRING", + "value" : "scripts" + }, + "sbncode_INCLUDE_DIR_INIT" : { + "type" : "STRING", + "value" : "include" + }, + "sbncode_LIBRARY_DIR_INIT" : { + "type" : "STRING", + "value" : "lib" + } + }, + "description" : "Configuration settings translated from ups/product_deps", + "displayName" : "Configuration from product_deps", + "hidden" : true, + "name" : "from_product_deps" + }, + { + "cacheVariables" : { + "CMAKE_CXX_COMPILER" : { + "type" : "STRING", + "value" : "$env{CETPKG_CXX_COMPILER}" + }, + "CMAKE_CXX_STANDARD" : { + "type" : "STRING", + "value" : "$env{CETPKG_CXX_STANDARD}" + }, + "CMAKE_C_COMPILER" : { + "type" : "STRING", + "value" : "$env{CETPKG_C_COMPILER}" + }, + "CMAKE_Fortran_COMPILER" : { + "type" : "STRING", + "value" : "$env{CETPKG_Fortran_COMPILER}" + }, + "UPS_CXX_COMPILER_ID" : { + "type" : "STRING", + "value" : "$env{CETPKG_CXX_COMPILER_ID}" + }, + "UPS_CXX_COMPILER_VERSION" : { + "type" : "STRING", + "value" : "$env{CETPKG_CXX_COMPILER_VERSION}" + }, + "UPS_C_COMPILER_ID" : { + "type" : "STRING", + "value" : "$env{CETPKG_C_COMPILER_ID}" + }, + "UPS_C_COMPILER_VERSION" : { + "type" : "STRING", + "value" : "$env{CETPKG_C_COMPILER_VERSION}" + }, + "UPS_Fortran_COMPILER_ID" : { + "type" : "STRING", + "value" : "$env{CETPKG_Fortran_COMPILER_ID}" + }, + "UPS_Fortran_COMPILER_VERSION" : { + "type" : "STRING", + "value" : "$env{CETPKG_Fortran_COMPILER_VERSION}" + }, + "WANT_UPS" : { + "type" : "BOOL", + "value" : true + }, + "sbncode_EXEC_PREFIX_INIT" : { + "type" : "STRING", + "value" : "$env{CETPKG_FQ_DIR}" + }, + "sbncode_UPS_BUILD_ONLY_DEPENDENCIES_INIT" : { + "type" : "STRING", + "value" : "cetmodules" + }, + "sbncode_UPS_PRODUCT_FLAVOR_INIT" : { + "type" : "STRING", + "value" : "$env{CETPKG_FLAVOR}" + }, + "sbncode_UPS_PRODUCT_NAME_INIT" : { + "type" : "STRING", + "value" : "sbncode" + }, + "sbncode_UPS_QUALIFIER_STRING_INIT" : { + "type" : "STRING", + "value" : "$env{CETPKG_QUALSPEC}" + } + }, + "description" : "Extra configuration for UPS package generation", + "displayName" : "UPS extra configuration", + "hidden" : true, + "name" : "extra_for_UPS" + }, + { + "description" : "Default configuration including settings from ups/product_deps", + "displayName" : "Default configuration", + "inherits" : "from_product_deps", + "name" : "default" + }, + { + "description" : "Default configuration for UPS package generation", + "displayName" : "Default configuration for UPS", + "inherits" : [ + "default", + "extra_for_UPS" + ], + "name" : "for_UPS" + } + ], + "version" : 3 +} diff --git a/fcl/CMakeLists.txt b/fcl/CMakeLists.txt index 3b6d71246..9a1d7be80 100644 --- a/fcl/CMakeLists.txt +++ b/fcl/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(gen) add_subdirectory(g4) add_subdirectory(caf) +add_subdirectory(util) install_fhicl() diff --git a/fcl/caf/cafmaker_common_defs.fcl b/fcl/caf/cafmaker_common_defs.fcl index 66fcdf7ab..b4d45abbf 100644 --- a/fcl/caf/cafmaker_common_defs.fcl +++ b/fcl/caf/cafmaker_common_defs.fcl @@ -1,3 +1,4 @@ +#include "eventweight_geant4_sbn.fcl" #include "eventweight_genie_sbn.fcl" #include "eventweight_flux_sbn.fcl" @@ -15,6 +16,7 @@ cafmaker_common_producers: { rns: { module_type: "RandomNumberSaver" } genieweight: @local::sbn_eventweight_genie fluxweight: @local::sbn_eventweight_flux + geant4weight: @local::sbn_eventweight_geant4 } END_PROLOG diff --git a/fcl/util/CMakeLists.txt b/fcl/util/CMakeLists.txt new file mode 100644 index 000000000..13355789a --- /dev/null +++ b/fcl/util/CMakeLists.txt @@ -0,0 +1 @@ +install_fhicl() diff --git a/fcl/util/dump_sbnjobmetadata.fcl b/fcl/util/dump_sbnjobmetadata.fcl new file mode 100644 index 000000000..2b14d3c68 --- /dev/null +++ b/fcl/util/dump_sbnjobmetadata.fcl @@ -0,0 +1,41 @@ +# +# File: dump_sbnjobmetadata.fcl +# Purpose: Prints on screen all the job SBN metadata stored in an input file. +# Author: Gianluca Petrillo (petrillo@slac.stanford.edu) +# Date: January 20, 2025 +# +# Reads and prints on screen the SBN job environment information from the input +# file. +# +# Changes +# -------- +# +# [20250120] (petrillo@slac.stanford.edu) +# original version +# + +#include "messages_icarus.fcl" + +process_name: JobMeta + +# use `icarus_message_services_interactive` to see dump on screen +services.message: @local::icarus_message_services_prod + +services.message.destinations.MetadataLog: { + type: file + filename: "JobEnvironment.log" + append: false + threshold: INFO + categories: { + DumpJobEnvironment: { limit: -1 } + default: { limit: 0 } + } +} + +outputs.metadataDumper: { + module_type: "DumpJobEnvironment" +} + +physics: { + streams: [ metadataDumper ] +} diff --git a/sbncode/BeamSpillInfoRetriever/BNBRetriever/BNBRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/BNBRetriever/BNBRetriever_module.cc deleted file mode 100644 index 2265d0e2c..000000000 --- a/sbncode/BeamSpillInfoRetriever/BNBRetriever/BNBRetriever_module.cc +++ /dev/null @@ -1,781 +0,0 @@ -/** ******************************************************************** - * @file BNBRetriever_module.cc - * @date Wed April 9 2021 - * @author J. Zennamo (FNAL) - * - * Based heavily on code by Z. Pavlovic written for MicroBooNE - * Based heavily on code by NOvA collaboration (Thanks NOvA!): - * https://cdcvs.fnal.gov/redmine/projects/novaart/repository/entry/trunk/IFDBSpillInfo/BNBInfo_module.cc - * Database implementation by Justin Mueller - */ - -#include "art/Framework/Core/EDProducer.h" -#include "art/Framework/Core/ModuleMacros.h" -#include "art/Framework/Principal/Event.h" -#include "art/Framework/Principal/Handle.h" -#include "art/Framework/Principal/Run.h" -#include "art/Framework/Principal/SubRun.h" -#include "canvas/Utilities/InputTag.h" -#include "fhiclcpp/types/Atom.h" -#include "messagefacility/MessageLogger/MessageLogger.h" -#include "larcorealg/CoreUtils/counter.h" - -#include "artdaq-core/Data/Fragment.hh" -#include "sbndaq-artdaq-core/Overlays/ICARUS/ICARUSTriggerV3Fragment.hh" -#include "sbnobj/Common/Trigger/ExtraTriggerInfo.h" - -#include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" - -#include "ifdh_art/IFBeamService/IFBeam_service.h" -#include "ifbeam_c.h" -#include "MWRData.h" - -#include -#include -#include - -#include -#include -#include - -namespace sbn { - class BNBRetriever; -} - -class sbn::BNBRetriever : public art::EDProducer { -public: - - struct Config { - - using Name = fhicl::Name; - using Comment = fhicl::Comment; - - fhicl::Atom TimePadding { - Name{ "TimePadding" }, - Comment{ "extension to the time window considered when collecting spills [seconds]" }, - 0.0333 // default - }; - - fhicl::Atom RawDataLabel { - Name{ "raw_data_label" }, - Comment{ "art data product instance name for trigger information (product label is 'daq')" } - }; - - fhicl::Atom DeviceUsedForTiming { - Name{ "DeviceUsedForTiming" }, - Comment{ "name in the IFBeam database of the device used to extract spill times" } - }; - - fhicl::Atom URL { - Name{ "URL" }, - Comment{ "IFBeam database access URL" } - }; - - fhicl::Atom Bundle { - Name{ "Bundle" }, - Comment{ "" } // explain what this is and which database/table it's looking for - }; - - fhicl::Atom TimeWindow { - Name{ "TimeWindow" }, - Comment{ "" } // explain what this is, what's for and its unit - }; - - fhicl::Atom MultiWireBundle { - Name{ "MultiWireBundle" }, - Comment{ "" } // explain what this is and which database/table it's looking for - }; - - fhicl::Atom MWR_TimeWindow { - Name{ "MWR_TimeWindow" }, - Comment{ "" } // explain what this is, what's for and its unit - }; - - fhicl::Atom TriggerDatabaseFile { - Name{ "TriggerDatabaseFile" }, - Comment{ "" } // explain what this is, what's for and its unit - }; - - - }; // Config - - using Parameters = art::EDProducer::Table; - - - explicit BNBRetriever(Parameters const& params); - // The compiler-generated destructor is fine for non-base - // classes without bare pointers or other resource use. - - // Plugins should not be copied or assigned. - BNBRetriever(BNBRetriever const&) = delete; - BNBRetriever(BNBRetriever&&) = delete; - BNBRetriever& operator=(BNBRetriever const&) = delete; - BNBRetriever& operator=(BNBRetriever&&) = delete; - - // Required functions. - void produce(art::Event& e) override; - void beginSubRun(art::SubRun& sr) override; - void endSubRun(art::SubRun& sr) override; - -private: - // input labels - std::vector< sbn::BNBSpillInfo > fOutbeamInfos; - double fTimePad; - std::string fURL; - MWRData mwrdata; - int run_number; - std::string raw_data_label; - std::string fDeviceUsedForTiming; - unsigned int TotalBeamSpills; - // - art::ServiceHandle ifbeam_handle; - std::unique_ptr bfp; - std::unique_ptr bfp_mwr; - - // - std::string fTriggerDatabaseFile; - sqlite3 *db; - int rc; - - struct TriggerInfo_t { - int gate_type = 0; ///< Source of the spill: `1`: BNB, `2`: NuMI - double t_current_event = 0; - double t_previous_event = 0; - unsigned int number_of_gates_since_previous_event = 0; // FIXME needs to be integral type - std::int64_t WR_to_Spill_conversion = 0; - }; - - struct MWRdata_t { - std::vector< std::vector > MWR_times; - std::vector< std::vector< std::vector< int > > > unpacked_MWR; - }; - - - static constexpr double MWRtoroidDelay = -0.035; ///< the same time point is measured _t_ by MWR and _t + MWRtoroidDelay`_ by the toroid [ms] - - /// Returns the information of the trigger in the current event. - TriggerInfo_t extractTriggerInfo(art::Event const& e) const; - - /** - * @brief Determines spill times and extracts data based on multiwire devices. - * @param triggerInfo information from the trigger of this event - * @return times and unpacked data, per device (`"M875BB"`, `"M876BB"`, `"MMBTBB"`) - */ - MWRdata_t extractSpillTimes(TriggerInfo_t const& triggerInfo) const; - - /** - * @brief Matches spill times with multiwire chamber data from the database. - * @param eventID ID of the event the information is associated to - * @param triggerInfo information from the trigger of this event - * @param MWRdata data from multiwire chambers - * @param isFirstEventInRun whether we are processing the first event of the run - * @param[out] beamInfos container to _add_ spill information records to - * @return count of matched spills - */ - int matchMultiWireData( - art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, bool isFirstEventInRun, - std::vector< sbn::BNBSpillInfo >& beamInfos - ) const; - - /** - * @brief Assembles and returns a spill information record. - * @param eventID ID of the event the information is associated to - * @param time time of the spill - * @param MWRdata all extracted data from multiwire chambers - * @param matched_MWR data from multiwire chambers matched with the time - * @return a `sbn::BNBSpillInfo` object with information on the spill at `time` - */ - sbn::BNBSpillInfo makeBNBSpillInfo - (art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR) const; - -/** - * @brief SQLite callback function for retrieving trigger_type from a query. - * @param data Pointer to the integer where the trigger type will be stored. - * @param argc Count of the number of columns returned by the query. - * @param argv Array of c-strings containing the column data of the query. - * @param columns Array of c-strings listing the names of the columns. - * @return 0 if successful. - */ -// int callback_trigger_type(void *data, -// int argc, -// char **argv, -// char **columns); - -/** - * @brief Queries the trigger database and finds the trigger_type of the matching trigger (if any). - * @param db The pointer to the SQLite database instance. - * @param run The run number of the current event (helps with queries). - * @param gate_time The time in milliseconds of the gate. - * @param threshold The required absolute time difference between gate and trigger. - * @return trigger_type -1: No matching trigger, 0: Majority, 1: MinBias - */ - int get_trigger_type_matching_gate(sqlite3 *db, - int func(void*,int,char**,char**), - int run, - long long int gate_time, - float threshold) const; - -}; - -int callback_trigger_type(void *data, int argc, char **argv, char **columns) -{ - int *result = static_cast(data); - // Does this query return non-NULL values? - if(argc > 0 && argv[0]) - *result = std::stoi(argv[0]); - else - *result = -1; - - return 0; -} - -int sbn::BNBRetriever::get_trigger_type_matching_gate(sqlite3 *db, int func(void*,int,char**,char**), int run, long long int gate_time, float threshold) const -{ - int trigger_type(-1), query_status; - std::stringstream query; - query << "SELECT trigger_type FROM triggerdata WHERE gate_type=1 AND run_number =" - << run - << " AND ABS(1000000000*wr_seconds + wr_nanoseconds - " - << std::fixed << gate_time - << ") < " - << threshold*1000000 - << " ORDER BY ABS(1000000000*wr_seconds + wr_nanoseconds - " - << std::fixed << gate_time - << ") LIMIT 1;"; - - query_status = sqlite3_exec(db, query.str().c_str(), func, &trigger_type, NULL); - if (query_status != SQLITE_OK) - { - mf::LogError("BNBEXTRetriever") << "SQL error: " << sqlite3_errmsg(db); - trigger_type = -1; - } - return trigger_type; -} - -sbn::BNBRetriever::BNBRetriever(Parameters const& params) - : EDProducer{params}, - fTimePad(params().TimePadding()), - raw_data_label(params().RawDataLabel()), - fDeviceUsedForTiming(params().DeviceUsedForTiming()), - bfp( ifbeam_handle->getBeamFolder(params().Bundle(), params().URL(), params().TimeWindow())), - bfp_mwr( ifbeam_handle->getBeamFolder(params().MultiWireBundle(), params().URL(), params().MWR_TimeWindow())), - fTriggerDatabaseFile(params().TriggerDatabaseFile()) -{ - - // Check fTimePad is positive - if (fTimePad < 0) { - throw art::Exception(art::errors::Configuration) - << "Parameter `TimePadding` must be non-negative (" << fTimePad << " was specified).\n"; - }//End Time check - - // how close in time does the spill time have to be from the DAQ time (in seconds). - // If these are too large then it fails to capture the device - // If these are too small then the time jitter in devices means we miss good data - // - // These values should likely not be changed unless authors of the IFBeam API are consulted - // - bfp->set_epsilon(0.02); //20 ms, this was tuned by hand and compared to IFBeamDB times - bfp_mwr->set_epsilon(0.5); - - //bfp_mwr->setValidWindow(86400); - bfp_mwr->setValidWindow(3605); - produces< std::vector< sbn::BNBSpillInfo >, art::InSubRun >(); - TotalBeamSpills = 0; - - cet::search_path sp("FW_SEARCH_PATH"); - std::string trigDB_path = sp.find_file(fTriggerDatabaseFile.c_str()); - - rc = sqlite3_open(trigDB_path.c_str(), &db); - if(rc) - { - fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); - throw art::Exception(art::errors::NotFound) - << "Can't open database: " << sqlite3_errmsg(db); - } - -} - - -void sbn::BNBRetriever::produce(art::Event& e) -{ - - // If this is the first event in the run, then ignore it - // We do not currently have the ability to figure out the first - // spill that the DAQ was sensitive to, so don't try to save any - // spill information - // - // TODO: long-term goal -- can we fix this? - // FIXME This is wrong.... - // Need to use: ICARUSTriggerV3Fragment long getTotalTriggerBNBMaj() const - - if (e.event() == 1) return; - - run_number = e.id().run(); - - TriggerInfo_t const triggerInfo = extractTriggerInfo(e); - - //We only want to process BNB gates, i.e. type 1 - if(triggerInfo.gate_type != 1) return; - // Keep track of the number of beam gates the DAQ thinks - // are in this job - TotalBeamSpills += triggerInfo.number_of_gates_since_previous_event; - - - MWRdata_t const MWRdata = extractSpillTimes(triggerInfo); - - - int const spill_count = matchMultiWireData(e.id(), triggerInfo, MWRdata, e.event() == 1, fOutbeamInfos); - - - if(spill_count > int(triggerInfo.number_of_gates_since_previous_event)) - mf::LogDebug("BNBRetriever")<< "Event Spills : " << spill_count << ", DAQ Spills : " << triggerInfo.number_of_gates_since_previous_event << " \t \t ::: WRONG!"<< std::endl; - else - mf::LogDebug("BNBRetriever")<< "Event Spills : " << spill_count << ", DAQ Spills : " << triggerInfo.number_of_gates_since_previous_event << std::endl; - -}//end iteration over art::Events - - -sbn::BNBRetriever::TriggerInfo_t sbn::BNBRetriever::extractTriggerInfo(art::Event const& e) const { - - //Here we read in the artdaq Fragments and extract three pieces of information: - // 1. The time of the current event, t_current_event - // 2. the time of the previously triggered event, t_previous_event (NOTE: Events are non-sequential!) - // 3. the number of beam spills since the previously triggered event, number_of_gates_since_previous_event - - auto const & raw_data = e.getProduct< std::vector >({ raw_data_label, "ICARUSTriggerV3" }); - auto const & extraTrigInfo = e.getProduct< sbn::ExtraTriggerInfo >("daqTrigger"); - - TriggerInfo_t triggerInfo; - - triggerInfo.WR_to_Spill_conversion = extraTrigInfo.WRtimeToTriggerTime; - - for(auto raw_datum : raw_data){ - - uint64_t artdaq_ts = raw_datum.timestamp(); - icarus::ICARUSTriggerV3Fragment frag(raw_datum); - std::string data = frag.GetDataString(); - char *buffer = const_cast(data.c_str()); - icarus::ICARUSTriggerInfo datastream_info = icarus::parse_ICARUSTriggerV3String(buffer); - triggerInfo.gate_type = datastream_info.gate_type; - triggerInfo.number_of_gates_since_previous_event = frag.getDeltaGatesBNBMaj(); - - /* - The DAQ trigger time is issued at the Beam Extraction Signal (BES) which is issued - 36 ms *after* the $1D of the BNB, which is what is used in the IFBeam database - - We subtract 36ms from the Trigger time to match our triggers to the spills in the - IFBeam database - - */ - - triggerInfo.t_current_event = static_cast(artdaq_ts-3.6e7)/(1000000000.0); //check this offset... - if(triggerInfo.gate_type == 1) - triggerInfo.t_previous_event = (static_cast(frag.getLastTimestampBNBMaj()-3.6e7))/(1e9); - else - triggerInfo.t_previous_event = (static_cast(frag.getLastTimestampOther()-3.6e7))/(1000000000.0); - - } - - mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Previous : " << triggerInfo.t_previous_event << ", Current : " << triggerInfo.t_current_event << ", Spill Count " << triggerInfo.number_of_gates_since_previous_event << std::endl; - - return triggerInfo; -} - - -sbn::BNBRetriever::MWRdata_t sbn::BNBRetriever::extractSpillTimes(TriggerInfo_t const& triggerInfo) const { - - // These lines get everything primed within the IFBeamDB - // They seem redundant but they are needed - // try{auto cur_vec_temp = bfp->GetNamedVector((triggerInfo.t_previous_event)-fTimePad,"E:THCURR");} catch (WebAPIException &we) {} - try{auto cur_vec_temp = bfp->GetNamedVector((triggerInfo.t_current_event)+fTimePad,"E:THCURR");} catch (WebAPIException &we) {} - try{auto packed_M876BB_temp = bfp_mwr->GetNamedVector((triggerInfo.t_current_event)+fTimePad,"E:M875BB{4440:888}.RAW");} catch (WebAPIException &we) {} - - //The multiwire chambers provide their - // data in a vector format but we'll have - // to sort through it in std::string format - // to correctly unpack it - std::vector< std::vector< std::vector< int > > > unpacked_MWR; - std::vector< std::vector< double> > MWR_times; - unpacked_MWR.resize(3); - MWR_times.resize(3); - std::string packed_data_str; - - //Create a list of all the MWR devices with their different - // memory buffer increments - // generally in the format: "E:.{Memory Block}" - std::vector vars = bfp_mwr->GetDeviceList(); - mf::LogDebug("BNBRetriever") << " Number of MWR Device Blocks Found : " << vars.size() << std::endl; - // Tracking the time from the IFBeamDB - double time_for_mwr; - - // this is an iterator to track which of the - // three devices we will be working with - int dev = 0; - - // The MWR devices are annoying and have confusing buffer - // what we'll do is sort through all of them first and then - // match them to the closest spills in time - // - - // int t_steps = int(((triggerInfo.t_previous_event - fTimePad) - (triggerInfo.t_current_event + fTimePad))/0.5)+25; - int t_steps = int(((triggerInfo.t_current_event + fTimePad) - (triggerInfo.t_previous_event - fTimePad - 20.))/0.5)+25; - mf::LogDebug("BNBRetriever") << " t_steps " << t_steps << std::endl; - - for(int t = 0; t < t_steps; t++){//Iterate through time increments - for (std::string const& var : vars) {// Iterate through the devices - - //Make sure we have a device - if(var.empty()){ - //mf::LogDebug("BNBRetriever") << " NO MWR DEVICES?!" << std::endl; - continue; - } - /// Check the device name and interate the double-vector index - if(var.find("M875BB") != std::string::npos ) dev = 0; - else if(var.find("M876BB") != std::string::npos ) dev = 1; - else if(var.find("MMBTBB") != std::string::npos ) dev = 2; - else{ - //mf::LogDebug("BNBRetriever") << " NOT matched to a MWR DEVICES?!" << var << std::endl; - continue;} - - time_for_mwr = 0; - - try{ - //Pull the MWR data for the device - // these data are "packed" - std::vector packed_MWR = bfp_mwr->GetNamedVector((triggerInfo.t_previous_event)-20.-fTimePad+double(0.5*t),var,&time_for_mwr); - - //We'll convert this into a format - // that we can unpack doubles >> strings - // - packed_data_str.clear(); - packed_data_str += std::to_string(int(time_for_mwr)); - packed_data_str.append(","); - packed_data_str.append(var); - packed_data_str.append(",,"); - - /* for(auto const value: packed_MWR){ - packed_data_str += ','; - packed_data_str += std::to_string(int(value)); - }*/ - for(int j = 0; j < int(packed_MWR.size()); j++){ - packed_data_str += std::to_string(int(packed_MWR[j])); - if(j < int(packed_MWR.size())-1) - packed_data_str.append(","); - } - - // Use Zarko's unpacking function to turn this into consumeable data - std::vector MWR_times_temp; - - // There is a 35 ms offset between the toriod and the MWR times - // we'll just remove that here to match to the spill times - std::vector< std::vector< int > > unpacked_MWR_temp = mwrdata.unpackMWR(packed_data_str,MWR_times_temp,MWRtoroidDelay); - - //There are four events that are packed into one MWR IFBeam entry - for(std::size_t s: util::counter(unpacked_MWR_temp.size())){ - - // If this entry has a unique time them store it for later - if(std::find(MWR_times[dev].begin(), MWR_times[dev].end(), MWR_times_temp[s]) == MWR_times[dev].end()){ - unpacked_MWR[dev].push_back(unpacked_MWR_temp[s]); - MWR_times[dev].push_back(MWR_times_temp[s]); - }//check for unique time - }//Iterate through the unpacked events - }//try - catch (WebAPIException &we) { - //Ignore when we can't find the MWR devices - // they don't always report and the timing of them can be annoying - - }//catch - }// Iterate over all the multiwire devices - }// Iterate over all times - - mf::LogDebug("BNBRetriever") << " Number of MWR[0] times : " << MWR_times[0].size() << std::endl; - mf::LogDebug("BNBRetriever") << " Number of MWR[0]s : " << unpacked_MWR[0].size() << std::endl; - mf::LogDebug("BNBRetriever") << " Number of MWR[1] times : " << MWR_times[1].size() << std::endl; - mf::LogDebug("BNBRetriever") << " Number of MWR[1]s : " << unpacked_MWR[1].size() << std::endl; - mf::LogDebug("BNBRetriever") << " Number of MWR[2] times : " << MWR_times[2].size() << std::endl; - mf::LogDebug("BNBRetriever") << " Number of MWR[2]s : " << unpacked_MWR[2].size() << std::endl; - - return { std::move(MWR_times), std::move(unpacked_MWR) }; -} - - -int sbn::BNBRetriever::matchMultiWireData( - art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, bool isFirstEventInRun, - std::vector< sbn::BNBSpillInfo >& beamInfos -) const { - - auto const& [ MWR_times, unpacked_MWR ] = MWRdata; // alias - - //Here we will start collecting all the other beamline devices - // First we get the times that the beamline device fired - // we have to pick a specific variable to use - std::vector times_temps = bfp->GetTimeList(fDeviceUsedForTiming); - - mf::LogDebug("BNBRetriever") << "matchMultiWireData:: Number of time spills : " << times_temps.size() << std::endl; - - // We'll keep track of how many of these spills match to our - // DAQ trigger times - int spill_count = 0; - int spills_removed = 0; - std::vector matched_MWR; - matched_MWR.resize(3); - - - // NOTE: for now, this is dead code because we don't - // do anything for the first event in a run. We may want to revisit - // this later to understand if there is a way we can do the POT - // accounting in the first event. - // - // Need to handle the first event in a run differently - if(isFirstEventInRun){ - - //We'll remove the spills after our event - int spills_after_our_target = 0; - // iterate through all the spills to find the - // spills that are after our triggered event - for (size_t i = 0; i < times_temps.size(); i++) { - if(times_temps[i] > (triggerInfo.t_current_event+fTimePad)){ - spills_after_our_target++; - } - }//end loop through spill times - - // Remove the spills after our trigger - times_temps.erase(times_temps.end()-spills_after_our_target,times_temps.end()); - - // Remove the spills before the start of our Run - times_temps.erase(times_temps.begin(), times_temps.end() - std::min(int(triggerInfo.number_of_gates_since_previous_event), int(times_temps.size()))); - - }//end fix for "first event" - - ///reject time_stamps which have a trigger_type == 1 from data-base - //To-Do - - // mf::LogDebug("BNBRetriever") << "Total number of Times we're going to test: " << times_temps.size() << std::endl; - // mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Upper Limit : " << (triggerInfo.t_current_event)+fTimePad << std::endl; - // mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Lower Limit : " << (triggerInfo.t_previous_event)+fTimePad << std::endl; - - // Iterating through each of the beamline times - for (size_t i = 0; i < times_temps.size(); i++) { - - // Only continue if these times are matched to our DAQ time - //mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Time # : " << i << std::endl; - - if(!isFirstEventInRun){//We already addressed the "first event" above - if(times_temps[i] > (triggerInfo.t_current_event)+fTimePad){ - //mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Removed! : " << times_temps[i] << std::endl; - spills_removed++; - continue;} - if(times_temps[i] <= (triggerInfo.t_previous_event)+fTimePad){ - spills_removed++; - //mf::LogDebug("BNBRetriever") << std::setprecision(19) << "Removed! : " << times_temps[i] << std::endl; - continue;} - } - - //check if this spill is is minbias - /* - 40 ms was selected to be close to but outside the 66 ms - time of the next spill (when the beam is running at 15 Hz) - DocDB 33155 provides documentation of this - */ - - mf::LogDebug("BNBRetriever") << std::setprecision(19) << "matchMultiWireData:: trigger type : " << get_trigger_type_matching_gate(db, callback_trigger_type, run_number, times_temps[i]*1.e9-triggerInfo.WR_to_Spill_conversion+3.6e7, 40.) << " times : spill " << times_temps[i]*1.e9 << " - " << triggerInfo.WR_to_Spill_conversion << " + " << 3.6e7 << std::endl; - - if(get_trigger_type_matching_gate(db, callback_trigger_type, run_number, times_temps[i]*1.e9-triggerInfo.WR_to_Spill_conversion+3.6e7, 40.) == 1){ - mf::LogDebug("BNBRetriever") << std::setprecision(19) << "matchMultiWireData:: Skipped a MinBias gate at : " << times_temps[i]*1000. << std::endl; - - continue; - } - - //Great we found a matched spill! Let's count it - spill_count++; - - //Loop through the multiwire devices: - - for(int dev = 0; dev < int(MWR_times.size()); dev++){ - - //Loop through the multiwire times: - double Tdiff = 1000000000.; - matched_MWR[dev] = 0; - - for(int mwrt = 0; mwrt < int(MWR_times[dev].size()); mwrt++){ - - //found a candidate match! - if(fabs((MWR_times[dev][mwrt] - times_temps[i])) >= Tdiff){continue;} - - bool best_match = true; - - //Check for a better match... - for (size_t j = 0; j < times_temps.size(); j++) { - if( j == i) continue; - if(times_temps[j] > (triggerInfo.t_current_event+fTimePad)){continue;} - if(times_temps[j] <= (triggerInfo.t_previous_event+fTimePad)){continue;} - - //is there a better match later in the spill sequence - if(fabs((MWR_times[dev][mwrt] - times_temps[j])) < - fabs((MWR_times[dev][mwrt] - times_temps[i]))){ - //we can have patience... - best_match = false; - break; - } - }//end better match check - - //Verified best match! - if(best_match == true){ - matched_MWR[dev] = mwrt; - Tdiff = fabs((MWR_times[dev][mwrt] - times_temps[i])); - } - - }//end loop over MWR times - - }//end loop over MWR devices - - sbn::BNBSpillInfo spillInfo = makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR); - - beamInfos.push_back(std::move(spillInfo)); - - // We do not write these to the art::Events because - // we can filter events but want to keep all the POT - // information, so we'll write it to the SubRun - - }//end iteration over beam device times - - // mf::LogDebug("BNBRetriever") << "matchMultiWireData:: Total spills counted: " << spill_count << " Total spills removed : " << spills_removed << std::endl; - - return spill_count; -} - - -sbn::BNBSpillInfo sbn::BNBRetriever::makeBNBSpillInfo - (art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR) const -{ - - auto const& [ MWR_times, unpacked_MWR ] = MWRdata; // alias - - // initializing all of our device carriers - // device definitions can be found in BNBSpillInfo.h - - double TOR860 = 0; // units e12 protons - double TOR875 = 0; // units e12 protons - double LM875A = 0; // units R/s - double LM875B = 0; // units R/s - double LM875C = 0; // units R/s - double HP875 = 0; // units mm - double VP875 = 0; // units mm - double HPTG1 = 0; // units mm - double VPTG1 = 0; // units mm - double HPTG2 = 0; // units mm - double VPTG2 = 0; // units mm - double BTJT2 = 0; // units Deg C - double THCURR = 0; // units kiloAmps - - double TOR860_time = 0; // units s - - // Here we request all the devices - // since sometimes devices fail to report we'll - // allow each to throw an exception but still move forward - // interpreting these failures will be part of the beam quality analyses - try{bfp->GetNamedData(time, "E:TOR860@",&TOR860,&TOR860_time);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:TOR875",&TOR875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875A",&LM875A);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875B",&LM875B);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875C",&LM875C);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HP875",&HP875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VP875",&VP875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HPTG1",&HPTG1);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VPTG1",&VPTG1);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HPTG2",&HPTG2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VPTG2",&VPTG2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:BTJT2",&BTJT2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:THCURR",&THCURR);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - - //crunch the times - unsigned long int time_closest_int = (int) TOR860_time; - double time_closest_ns = (TOR860_time - time_closest_int)*1e9; - - //Store everything in our data-product - sbn::BNBSpillInfo beamInfo; - beamInfo.TOR860 = TOR860*1e12; //add in factor of 1e12 protons to get correct POT units - beamInfo.TOR875 = TOR875*1e12; //add in factor of 1e12 protons to get correct POT units - beamInfo.LM875A = LM875A; - beamInfo.LM875B = LM875B; - beamInfo.LM875C = LM875C; - beamInfo.HP875 = HP875; - beamInfo.VP875 = VP875; - beamInfo.HPTG1 = HPTG1; - beamInfo.VPTG1 = VPTG1; - beamInfo.HPTG2 = HPTG2; - beamInfo.VPTG2 = VPTG2; - beamInfo.BTJT2 = BTJT2; - beamInfo.THCURR = THCURR; - beamInfo.spill_time_s = time_closest_int; - beamInfo.spill_time_ns = time_closest_ns; - - for(auto const& MWRdata: unpacked_MWR){ - std::ignore = MWRdata; - assert(!MWRdata.empty()); - } - - if(unpacked_MWR[0].empty()){ - beamInfo.M875BB.clear(); - beamInfo.M875BB_spill_time_diff = -999;//units in seconds - } - else{ - beamInfo.M875BB = unpacked_MWR[0][matched_MWR[0]]; - beamInfo.M875BB_spill_time_diff = (MWR_times[0][matched_MWR[0]] - time); - } - - if(unpacked_MWR[1].empty()){ - beamInfo.M876BB.clear(); - beamInfo.M876BB_spill_time_diff = -999;//units in seconds - } - else{ - beamInfo.M876BB = unpacked_MWR[1][matched_MWR[1]]; - beamInfo.M876BB_spill_time_diff = (MWR_times[1][matched_MWR[1]] - time); - } - - if(unpacked_MWR[2].empty()){ - beamInfo.MMBTBB.clear(); - beamInfo.MMBTBB_spill_time_diff = -999;//units in seconds - } - else{ - beamInfo.MMBTBB = unpacked_MWR[2][matched_MWR[2]]; - beamInfo.MMBTBB_spill_time_diff = (MWR_times[2][matched_MWR[2]] - time); - } - // We do not write these to the art::Events because - // we can filter events but want to keep all the POT - // information, so we'll write it to the SubRun - - beamInfo.event = eventID.event(); // the rest of ID is known by art::SubRun - - return beamInfo; -} - - -void sbn::BNBRetriever::beginSubRun(art::SubRun& sr) -{ - return; -} - -//____________________________________________________________________________ -void sbn::BNBRetriever::endSubRun(art::SubRun& sr) -{ - // We will add all of the BNBSpillInfo data-products to the - // art::SubRun so it persists - // currently this is ~2.7 kB/event or ~0.07 kB/spill - -mf::LogDebug("BNBRetriever")<< "Total number of DAQ Spills : " << TotalBeamSpills << std::endl; -mf::LogDebug("BNBRetriever")<< "Total number of Selected Spills : " << fOutbeamInfos.size() << std::endl; - - auto p = std::make_unique< std::vector< sbn::BNBSpillInfo > >(); - std::swap(*p, fOutbeamInfos); - - sr.put(std::move(p), art::subRunFragment()); - - return; -} - -DEFINE_ART_MODULE(sbn::BNBRetriever) diff --git a/sbncode/BeamSpillInfoRetriever/BNBRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/BNBRetriever/CMakeLists.txt deleted file mode 100644 index a34b60d6a..000000000 --- a/sbncode/BeamSpillInfoRetriever/BNBRetriever/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -find_package(ifbeam) -find_package(ifdh_art) - - -art_make_library(LIBRARIES Boost::system - LIBRARY_NAME sbn_BNBSpillInfoRetriever_MWRData - SOURCE MWRData.cpp -) - - -cet_build_plugin(BNBRetriever art::module - LIBRARIES - art::Persistency_Common - art::Utilities canvas::canvas - cetlib::cetlib cetlib_except::cetlib_except - ROOT::X3d - Boost::system - messagefacility::MF_MessageLogger - ifbeam::ifbeam - ifdh_art::IFBeam_service - SQLite::SQLite3 - sbndaq_artdaq_core::sbndaq-artdaq-core_Overlays_Common - sbndaq_artdaq_core::sbndaq-artdaq-core_Overlays - sbndaq_artdaq_core::sbndaq-artdaq-core_Overlays_ICARUS - artdaq_core::artdaq-core_Utilities - sbn_BNBSpillInfoRetriever_MWRData - sbnobj::Common_POTAccounting - larcorealg::CoreUtils -) - -install_headers() -install_fhicl() -install_source() - diff --git a/sbncode/BeamSpillInfoRetriever/BNBRetriever/MWRData.cpp b/sbncode/BeamSpillInfoRetriever/BNBRetriever/MWRData.cpp deleted file mode 100644 index 48d0bda3f..000000000 --- a/sbncode/BeamSpillInfoRetriever/BNBRetriever/MWRData.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "MWRData.h" - -using namespace std; - -namespace sbn{ - - std::vector< std::vector < int > > MWRData::unpackMWR(std::string packed_data, std::vector &time_stamp, double timeoffset) const -{ - - std::vector > unpacked_data; - unpacked_data.resize(4); - short data[444]; - - std::vector row(0); - boost::split(row, packed_data, boost::is_any_of(",")); - if (row.size()==447) { - for (int i=3;i<447;i++) { - data[i-3]=atoi(row[i].c_str()); - } - string devname=row[1].substr(0,8); - for (int idev=0;idev<4;idev++) { - mwrpulse_t mwr=getMWRdata(data,idev); - time_stamp.push_back(mwr.sheader.timesec+mwr.sheader.timensec/1000000000.+timeoffset); - for (int ich=0;ich<48;ich++) { - unpacked_data[idev].push_back(mwr.hor[ich]); - } - for (int ich=0;ich<48;ich++) { - unpacked_data[idev].push_back(mwr.ver[ich]); - } - } - } else { - cout <<"BeamSpillInfoRetriever: MRWData: Bad data!"< -namespace sbn{ -class MWRData -{ - typedef struct swicheader_t { - long timesec; - long timensec; - long gpstime1; - long gpstime2; - short boosterevent; - short mievent; - short hz15micnt; - long delta1f; - short pulsemi; - short pulsesc; - } swicheader_t; - - typedef struct mwrpulse_t { - short hor[48]; - short ver[48]; - swicheader_t sheader; - } mwrpulse_t; - - static long flipByte(long data) - { - return ((data>>16)&0x0000FFFF) | ((data<<16)&0xFFFF0000); - } - - mwrpulse_t getMWRdata(short* data, int nblock) const; - - public: - std::vector< std::vector < int > > unpackMWR(std::string packed_data, std::vector &time_stamp, double timeoffset=0) const; -}; -} - -#endif /* #ifndef _MWRDATA_H */ diff --git a/sbncode/BeamSpillInfoRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/CMakeLists.txt index 3fcd2f930..e7fd09719 100644 --- a/sbncode/BeamSpillInfoRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/CMakeLists.txt @@ -1,9 +1,9 @@ -add_subdirectory(BNBRetriever) +add_subdirectory(ICARUSBNBRetriever) +add_subdirectory(ICARUSBNBEXTRetriever) add_subdirectory(SBNDBNBRetriever) -add_subdirectory(SBNDBNBZEROBIASRetriever) add_subdirectory(SBNDBNBEXTRetriever) +add_subdirectory(SBNDBNBZEROBIASRetriever) add_subdirectory(NuMIRetriever) -add_subdirectory(BNBEXTRetriever) add_subdirectory(NuMIEXTRetriever) add_subdirectory(job) @@ -15,6 +15,7 @@ art_make_library(LIBRARIES Boost::system SOURCE MWRData.cpp ) + art_make_library( LIBRARIES art::Persistency_Common @@ -26,9 +27,20 @@ art_make_library( sbnobj::Common_POTAccounting sbn_MWRData larcorealg::CoreUtils + LIBRARY_NAME sbn_POTTools + SOURCE POTTools.cpp +) + +art_make_library( + LIBRARIES + + ROOT::Hist + ROOT::MathCore + sbnobj::Common_POTAccounting + - LIBRARY_NAME sbn_SBNDPOTTools - SOURCE SBNDPOTTools.cpp + LIBRARY_NAME sbn_getFOM + SOURCE getFOM.cpp ) install_headers() diff --git a/sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/CMakeLists.txt similarity index 88% rename from sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/CMakeLists.txt rename to sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/CMakeLists.txt index 1c49f8646..b1433c96b 100644 --- a/sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/CMakeLists.txt @@ -1,6 +1,6 @@ -cet_build_plugin(BNBEXTRetriever art::module - LIBRARIES +cet_build_plugin(ICARUSBNBEXTRetriever art::module + LIBRARIES PRIVATE art::Persistency_Common art::Utilities canvas::canvas cetlib::cetlib cetlib_except::cetlib_except diff --git a/sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/BNBEXTRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/ICARUSBNBEXTRetriever_module.cc similarity index 86% rename from sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/BNBEXTRetriever_module.cc rename to sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/ICARUSBNBEXTRetriever_module.cc index 1b7c79dd5..3bc4ef8d8 100644 --- a/sbncode/BeamSpillInfoRetriever/BNBEXTRetriever/BNBEXTRetriever_module.cc +++ b/sbncode/BeamSpillInfoRetriever/ICARUSBNBEXTRetriever/ICARUSBNBEXTRetriever_module.cc @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////// -// Class: BNBEXTRetriever +// Class: ICARUSBNBEXTRetriever // Plugin Type: producer -// File: BNBEXTRetriever_module.cc +// File: ICARUSBNBEXTRetriever_module.cc // // Created by hand Thurs June 24th 2021 by J. Zennamo (FNAL) // @@ -36,10 +36,10 @@ #include namespace sbn { - class BNBEXTRetriever; + class ICARUSBNBEXTRetriever; } -class sbn::BNBEXTRetriever : public art::EDProducer { +class sbn::ICARUSBNBEXTRetriever : public art::EDProducer { public: struct Config { @@ -57,15 +57,15 @@ class sbn::BNBEXTRetriever : public art::EDProducer { using Parameters = art::EDProducer::Table; - explicit BNBEXTRetriever(Parameters const& params); + explicit ICARUSBNBEXTRetriever(Parameters const& params); // The compiler-generated destructor is fine for non-base // classes without bare pointers or other resource use. // Plugins should not be copied or assigned. - BNBEXTRetriever(BNBEXTRetriever const&) = delete; - BNBEXTRetriever(BNBEXTRetriever&&) = delete; - BNBEXTRetriever& operator=(BNBEXTRetriever const&) = delete; - BNBEXTRetriever& operator=(BNBEXTRetriever&&) = delete; + ICARUSBNBEXTRetriever(ICARUSBNBEXTRetriever const&) = delete; + ICARUSBNBEXTRetriever(ICARUSBNBEXTRetriever&&) = delete; + ICARUSBNBEXTRetriever& operator=(ICARUSBNBEXTRetriever const&) = delete; + ICARUSBNBEXTRetriever& operator=(ICARUSBNBEXTRetriever&&) = delete; // Required functions. void produce(art::Event& e) override; @@ -85,7 +85,7 @@ class sbn::BNBEXTRetriever : public art::EDProducer { }; -sbn::BNBEXTRetriever::BNBEXTRetriever(Parameters const& params) +sbn::ICARUSBNBEXTRetriever::ICARUSBNBEXTRetriever(Parameters const& params) : EDProducer{params}, raw_data_label_(params().RawDataLabel()) { @@ -97,7 +97,7 @@ sbn::BNBEXTRetriever::BNBEXTRetriever(Parameters const& params) scale_factor = 0; } -void sbn::BNBEXTRetriever::produce(art::Event& e) +void sbn::ICARUSBNBEXTRetriever::produce(art::Event& e) { //Here we read in the artdaq Fragments and extract three pieces of information: @@ -152,7 +152,7 @@ void sbn::BNBEXTRetriever::produce(art::Event& e) } //end loop over events -void sbn::BNBEXTRetriever::beginSubRun(art::SubRun& sr) +void sbn::ICARUSBNBEXTRetriever::beginSubRun(art::SubRun& sr) { TotalEXTCounts = 0; totalMinBias = 0; @@ -162,7 +162,7 @@ void sbn::BNBEXTRetriever::beginSubRun(art::SubRun& sr) } //____________________________________________________________________________ -void sbn::BNBEXTRetriever::endSubRun(art::SubRun& sr) +void sbn::ICARUSBNBEXTRetriever::endSubRun(art::SubRun& sr) { // We will add all of the EXTCountInfo data-products to the // art::SubRun so it persists @@ -173,7 +173,7 @@ void sbn::BNBEXTRetriever::endSubRun(art::SubRun& sr) std::cout << "FAILED! " << std::endl; // probably want to throw an exception here - for(auto ExtInfo : fOutExtInfos){ + for(auto &ExtInfo : fOutExtInfos){ ExtInfo.gates_since_last_trigger *= scale_factor; @@ -186,4 +186,4 @@ void sbn::BNBEXTRetriever::endSubRun(art::SubRun& sr) return; } -DEFINE_ART_MODULE(sbn::BNBEXTRetriever) +DEFINE_ART_MODULE(sbn::ICARUSBNBEXTRetriever) diff --git a/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/CMakeLists.txt new file mode 100644 index 000000000..104109bc1 --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/CMakeLists.txt @@ -0,0 +1,13 @@ +find_package(ifbeam) +find_package(ifdh_art) + +cet_build_plugin(ICARUSBNBRetriever art::module + LIBRARIES PRIVATE + sbn_POTTools + sbn_getFOM + SQLite::SQLite3 +) + +install_headers() +install_fhicl() +install_source() diff --git a/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/ICARUSBNBRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/ICARUSBNBRetriever_module.cc new file mode 100644 index 000000000..077eb4c9e --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/ICARUSBNBRetriever/ICARUSBNBRetriever_module.cc @@ -0,0 +1,493 @@ +/** ******************************************************************** + * @file ICARUSBNBRetriever_module.cc + * @date Wed April 9 2021 + * @author J. Zennamo (FNAL) + * + * Based heavily on code by Z. Pavlovic written for MicroBooNE + * Based heavily on code by NOvA collaboration (Thanks NOvA!): + * https://cdcvs.fnal.gov/redmine/projects/novaart/repository/entry/trunk/IFDBSpillInfo/BNBInfo_module.cc + * Database implementation by Justin Mueller + */ + +#include "art/Framework/Core/EDProducer.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" +#include "sbnobj/Common/Trigger/ExtraTriggerInfo.h" +#include "sbndaq-artdaq-core/Overlays/ICARUS/ICARUSTriggerV3Fragment.hh" +#include +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" +#include "sbncode/BeamSpillInfoRetriever/getFOM.h" + +namespace sbn { + class ICARUSBNBRetriever; +} + +class sbn::ICARUSBNBRetriever : public art::EDProducer { +public: + + struct Config { + + using Name = fhicl::Name; + using Comment = fhicl::Comment; + + fhicl::Atom TimePadding { + Name{ "TimePadding" }, + Comment{ "extension to the time window considered when collecting spills [seconds]" }, + 0.0333 // default + }; + + fhicl::Atom BESOffset { + Name{ "BESOffset" }, + Comment{ "Offset between beam early warning signal and $1D [seconds]" }, + }; + + fhicl::Atom RawDataLabel { + Name{ "raw_data_label" }, + Comment{ "art data product instance name for trigger information (product label is 'daq')" } + }; + + fhicl::Atom DeviceUsedForTiming { + Name{ "DeviceUsedForTiming" }, + Comment{ "name in the IFBeam database of the device used to extract spill times" } + }; + + fhicl::Atom URL { + Name{ "URL" }, + Comment{ "IFBeam database access URL" } + }; + + fhicl::Atom Bundle { + Name{ "Bundle" }, + Comment{ "Name of the collection of devices to grab from IFbeam for everything except MWRdevices." } + }; + + fhicl::Atom TimeWindow { + Name{ "TimeWindow" }, + Comment{ "Window of time in seconds to use for non-mwr ifbeam queries." } + }; + + fhicl::Atom MultiWireBundle { + Name{ "MultiWireBundle" }, + Comment{ "name of collections of devices to grab from IFbeam for use as MWRdevices." } + }; + + fhicl::Atom VP873Bundle { + Name{ "VP873Bundle" }, + Comment{ "name of collections of devices to grab from IFbeam for use as VP873." } + }; + + fhicl::Atom OffsetBundle { + Name{ "OffsetBundle" }, + Comment{ "name of collections of devices to grab from IFbeam for use for offsets." } + }; + fhicl::Atom MWR_TimeWindow { + Name{ "MWR_TimeWindow" }, + Comment{ "Window of time in seconds to use for mwr ifbeam queries." } + }; + + fhicl::Atom TriggerDatabaseFile { + Name{ "TriggerDatabaseFile" }, + Comment{ "path of local database of all recorded events and their trigger, in SQLite format" } + }; + + + }; // Config + + using Parameters = art::EDProducer::Table; + + + explicit ICARUSBNBRetriever(Parameters const& params); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + ICARUSBNBRetriever(ICARUSBNBRetriever const&) = delete; + ICARUSBNBRetriever(ICARUSBNBRetriever&&) = delete; + ICARUSBNBRetriever& operator=(ICARUSBNBRetriever const&) = delete; + ICARUSBNBRetriever& operator=(ICARUSBNBRetriever&&) = delete; + + // Required functions. + void produce(art::Event& e) override; + void beginSubRun(art::SubRun& sr) override; + void endSubRun(art::SubRun& sr) override; + +private: + // input labels + std::vector< sbn::BNBSpillInfo > fOutbeamInfos; + double fTimePad; + double fBESOffset; + std::string fURL; + MWRData mwrdata; + int run_number; + std::string raw_data_label; + std::string fDeviceUsedForTiming; + unsigned int TotalBeamSpills; + // + art::ServiceHandle ifbeam_handle; + std::unique_ptr bfp; + std::unique_ptr vp873; + std::unique_ptr offsets; + std::unique_ptr bfp_mwr; + + // + std::string fTriggerDatabaseFile; + sqlite3 *db; + int rc; + + static constexpr double MWRtoroidDelay = -0.035; ///< the same time point is measured _t_ by MWR and _t + MWRtoroidDelay`_ by the toroid [ms] + + /// Returns the information of the trigger in the current event. + sbn::pot::TriggerInfo_t extractTriggerInfo(art::Event const& e) const; + /** + * @brief Matches spill times with multiwire chamber data from the database. + * @param eventID ID of the event the information is associated to + * @param triggerInfo information from the trigger of this event + * @param MWRdata data from multiwire chambers + * @param isFirstEventInRun whether we are processing the first event of the run + * @param[out] beamInfos container to _add_ spill information records to + * @return count of matched spills + */ + int matchMultiWireData( + art::EventID const& eventID, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, + std::vector< sbn::BNBSpillInfo >& beamInfos + ) const; + +/** + * @brief SQLite callback function for retrieving trigger_type from a query. + * @param data Pointer to the integer where the trigger type will be stored. + * @param argc Count of the number of columns returned by the query. + * @param argv Array of c-strings containing the column data of the query. + * @param columns Array of c-strings listing the names of the columns. + * @return 0 if successful. + */ +// int callback_trigger_type(void *data, +// int argc, +// char **argv, +// char **columns); + +/** + * @brief Queries the trigger database and finds the trigger_type of the matching trigger (if any). + * @param db The pointer to the SQLite database instance. + * @param run The run number of the current event (helps with queries). + * @param gate_time The time in milliseconds of the gate. + * @param threshold The required absolute time difference between gate and trigger. + * @return trigger_type -1: No matching trigger, 0: Majority, 1: MinBias + */ + int get_trigger_type_matching_gate(sqlite3 *db, + int func(void*,int,char**,char**), + int run, + long long int gate_time, + float threshold) const; + +}; + +int callback_trigger_type(void *data, int argc, char **argv, char **columns) +{ + int *result = static_cast(data); + // Does this query return non-NULL values? + if(argc > 0 && argv[0]) + *result = std::stoi(argv[0]); + else + *result = -1; + + return 0; +} + +int sbn::ICARUSBNBRetriever::get_trigger_type_matching_gate(sqlite3 *db, int func(void*,int,char**,char**), int run, long long int gate_time, float threshold) const +{ + int trigger_type(-1), query_status; + std::stringstream query; + query << "SELECT trigger_type FROM triggerdata WHERE gate_type=1 AND run_number =" + << run + << " AND ABS(1000000000*wr_seconds + wr_nanoseconds - " + << std::fixed << gate_time + << ") < " + << threshold*1000000 + << " ORDER BY ABS(1000000000*wr_seconds + wr_nanoseconds - " + << std::fixed << gate_time + << ") LIMIT 1;"; + + query_status = sqlite3_exec(db, query.str().c_str(), func, &trigger_type, NULL); + if (query_status != SQLITE_OK) + { + mf::LogError("BNBEXTRetriever") << "SQL error: " << sqlite3_errmsg(db); + trigger_type = -1; + } + return trigger_type; +} + +sbn::ICARUSBNBRetriever::ICARUSBNBRetriever(Parameters const& params) + : EDProducer{params}, + fTimePad(params().TimePadding()), + fBESOffset(params().BESOffset()), + raw_data_label(params().RawDataLabel()), + fDeviceUsedForTiming(params().DeviceUsedForTiming()), + bfp( ifbeam_handle->getBeamFolder(params().Bundle(), params().URL(), params().TimeWindow())), + vp873( ifbeam_handle->getBeamFolder(params().VP873Bundle(), params().URL(), params().TimeWindow())), + offsets( ifbeam_handle->getBeamFolder(params().OffsetBundle(), params().URL(), params().TimeWindow())), + bfp_mwr( ifbeam_handle->getBeamFolder(params().MultiWireBundle(), params().URL(), params().MWR_TimeWindow())), + fTriggerDatabaseFile(params().TriggerDatabaseFile()) +{ + + // Check fTimePad is positive + if (fTimePad < 0) { + throw art::Exception(art::errors::Configuration) + << "Parameter `TimePadding` must be non-negative (" << fTimePad << " was specified).\n"; + }//End Time check + + // how close in time does the spill time have to be from the DAQ time (in seconds). + // If these are too large then it fails to capture the device + // If these are too small then the time jitter in devices means we miss good data + // + // These values should likely not be changed unless authors of the IFBeam API are consulted + // + bfp->set_epsilon(0.02); //20 ms, this was tuned by hand and compared to IFBeamDB times + bfp_mwr->set_epsilon(0.5); + vp873->set_epsilon(0.02); + offsets->set_epsilon(600); + + //bfp_mwr->setValidWindow(86400); + bfp_mwr->setValidWindow(3605); + produces< std::vector< sbn::BNBSpillInfo >, art::InSubRun >(); + TotalBeamSpills = 0; + + cet::search_path sp("FW_SEARCH_PATH"); + std::string trigDB_path = sp.find_file(fTriggerDatabaseFile.c_str()); + + rc = sqlite3_open(trigDB_path.c_str(), &db); + if(rc) + { + fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); + throw art::Exception(art::errors::NotFound) + << "Can't open database: " << sqlite3_errmsg(db); + } + +} + + +void sbn::ICARUSBNBRetriever::produce(art::Event& e) +{ + + // If this is the first event in the run, then ignore it + // We do not currently have the ability to figure out the first + // spill that the DAQ was sensitive to, so don't try to save any + // spill information + + if (e.event() == 1) return; + + run_number = e.id().run(); + + sbn::pot::TriggerInfo_t const triggerInfo = extractTriggerInfo(e); + + //We only want to process BNB gates, i.e. type 1 + if(triggerInfo.gate_type != 1) return; + // Keep track of the number of beam gates the DAQ thinks + // are in this job + TotalBeamSpills += triggerInfo.number_of_gates_since_previous_event; + sbn::pot::MWRdata_t const MWRdata = extractSpillTimes(triggerInfo, bfp, bfp_mwr, fTimePad, MWRtoroidDelay, mwrdata); + + int const spill_count = matchMultiWireData(e.id(), triggerInfo, MWRdata, fOutbeamInfos); + + if(spill_count > int(triggerInfo.number_of_gates_since_previous_event)) + mf::LogDebug("ICARUSBNBRetriever")<< "Event Spills : " << spill_count << ", DAQ Spills : " << triggerInfo.number_of_gates_since_previous_event << " \t \t ::: WRONG!"<< std::endl; + else + mf::LogDebug("ICARUSBNBRetriever")<< "Event Spills : " << spill_count << ", DAQ Spills : " << triggerInfo.number_of_gates_since_previous_event << std::endl; + +}//end iteration over art::Events + + +sbn::pot::TriggerInfo_t sbn::ICARUSBNBRetriever::extractTriggerInfo(art::Event const& e) const { + + //Here we read in the artdaq Fragments and extract three pieces of information: + // 1. The time of the current event, t_current_event + // 2. the time of the previously triggered event, t_previous_event (NOTE: Events are non-sequential!) + // 3. the number of beam spills since the previously triggered event, number_of_gates_since_previous_event + + auto const & raw_data = e.getProduct< std::vector >({ raw_data_label, "ICARUSTriggerV3" }); + auto const & extraTrigInfo = e.getProduct< sbn::ExtraTriggerInfo >("daqTrigger"); + + sbn::pot::TriggerInfo_t triggerInfo; + + triggerInfo.WR_to_Spill_conversion = extraTrigInfo.WRtimeToTriggerTime; + + for(auto raw_datum : raw_data){ + + uint64_t artdaq_ts = raw_datum.timestamp(); + icarus::ICARUSTriggerV3Fragment frag(raw_datum); + std::string data = frag.GetDataString(); + char *buffer = const_cast(data.c_str()); + icarus::ICARUSTriggerInfo datastream_info = icarus::parse_ICARUSTriggerV3String(buffer); + triggerInfo.gate_type = datastream_info.gate_type; + triggerInfo.number_of_gates_since_previous_event = frag.getDeltaGatesBNBMaj(); + + /* + The DAQ trigger time is issued at the Beam Extraction Signal (BES) which is issued + 36 ms *after* the $1D of the BNB, which is what is used in the IFBeam database + + We subtract 36ms from the Trigger time to match our triggers to the spills in the + IFBeam database + + */ + + double BESinTSUnits = fBESOffset*1e9; // flc param is in seconds need to convert to match TS + triggerInfo.t_current_event = static_cast(artdaq_ts-BESinTSUnits)/(1000000000.0); //check this offset... + if(triggerInfo.gate_type == 1) + triggerInfo.t_previous_event = (static_cast(frag.getLastTimestampBNBMaj()-BESinTSUnits))/(1e9); + else + triggerInfo.t_previous_event = (static_cast(frag.getLastTimestampOther()-BESinTSUnits))/(1000000000.0); + + } + + mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Previous : " << triggerInfo.t_previous_event << ", Current : " << triggerInfo.t_current_event << ", Spill Count " << triggerInfo.number_of_gates_since_previous_event << std::endl; + + return triggerInfo; +} + +int sbn::ICARUSBNBRetriever::matchMultiWireData( + art::EventID const& eventID, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, + std::vector< sbn::BNBSpillInfo >& beamInfos +) const { + + auto const& [ MWR_times, unpacked_MWR ] = MWRdata; // alias + + //Here we will start collecting all the other beamline devices + // First we get the times that the beamline device fired + // we have to pick a specific variable to use + std::vector times_temps = bfp->GetTimeList(fDeviceUsedForTiming); + + mf::LogDebug("ICARUSBNBRetriever") << "matchMultiWireData:: Number of time spills : " << times_temps.size() << std::endl; + + // We'll keep track of how many of these spills match to our + // DAQ trigger times + int spill_count = 0; + int spills_removed = 0; + std::vector matched_MWR; + matched_MWR.resize(3); + + ///reject time_stamps which have a trigger_type == 1 from data-base + //To-Do + + // mf::LogDebug("ICARUSBNBRetriever") << "Total number of Times we're going to test: " << times_temps.size() << std::endl; + // mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Upper Limit : " << (triggerInfo.t_current_event)+fTimePad << std::endl; + // mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Lower Limit : " << (triggerInfo.t_previous_event)+fTimePad << std::endl; + + // Iterating through each of the beamline times + for (size_t i = 0; i < times_temps.size(); i++) { + + // Only continue if these times are matched to our DAQ time + //mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Time # : " << i << std::endl; + + if(times_temps[i] > (triggerInfo.t_current_event)+fTimePad){ + //mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Removed! : " << times_temps[i] << std::endl; + spills_removed++; + continue;} + if(times_temps[i] <= (triggerInfo.t_previous_event)+fTimePad){ + spills_removed++; + //mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "Removed! : " << times_temps[i] << std::endl; + continue;} + + //check if this spill is is minbias + /* + 40 ms was selected to be close to but outside the 66 ms + time of the next spill (when the beam is running at 15 Hz) + DocDB 33155 provides documentation of this + */ + + double BESinTSUnits = fBESOffset*1e9; // flc param is in seconds need to convert to match TS + mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "matchMultiWireData:: trigger type : " << get_trigger_type_matching_gate(db, callback_trigger_type, run_number, times_temps[i]*1.e9-triggerInfo.WR_to_Spill_conversion+BESinTSUnits, 40.) << " times : spill " << times_temps[i]*1.e9 << " - " << triggerInfo.WR_to_Spill_conversion << " + " << BESinTSUnits << std::endl; + + if(get_trigger_type_matching_gate(db, callback_trigger_type, run_number, times_temps[i]*1.e9-triggerInfo.WR_to_Spill_conversion+BESinTSUnits, 40.) == 1){ + mf::LogDebug("ICARUSBNBRetriever") << std::setprecision(19) << "matchMultiWireData:: Skipped a MinBias gate at : " << times_temps[i]*1000. << std::endl; + + continue; + } + + //Great we found a matched spill! Let's count it + spill_count++; + + //Loop through the multiwire devices: + + for(int dev = 0; dev < int(MWR_times.size()); dev++){ + + //Loop through the multiwire times: + double Tdiff = 1000000000.; + matched_MWR[dev] = 0; + + for(int mwrt = 0; mwrt < int(MWR_times[dev].size()); mwrt++){ + + //found a candidate match! + if(fabs((MWR_times[dev][mwrt] - times_temps[i])) >= Tdiff){continue;} + + bool best_match = true; + + //Check for a better match... + for (size_t j = 0; j < times_temps.size(); j++) { + if( j == i) continue; + if(times_temps[j] > (triggerInfo.t_current_event+fTimePad)){continue;} + if(times_temps[j] <= (triggerInfo.t_previous_event+fTimePad)){continue;} + + //is there a better match later in the spill sequence + if(fabs((MWR_times[dev][mwrt] - times_temps[j])) < + fabs((MWR_times[dev][mwrt] - times_temps[i]))){ + //we can have patience... + best_match = false; + break; + } + }//end better match check + + //Verified best match! + if(best_match == true){ + matched_MWR[dev] = mwrt; + Tdiff = fabs((MWR_times[dev][mwrt] - times_temps[i])); + } + + }//end loop over MWR times + + }//end loop over MWR devices + + sbn::BNBSpillInfo spillInfo = sbn::pot::makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR, bfp, offsets, vp873); + double const spillFOM = sbn::getBNBqualityFOM(spillInfo); + spillInfo.FOM = spillFOM; + + beamInfos.push_back(std::move(spillInfo)); + + // We do not write these to the art::Events because + // we can filter events but want to keep all the POT + // information, so we'll write it to the SubRun + + }//end iteration over beam device times + + // mf::LogDebug("ICARUSBNBRetriever") << "matchMultiWireData:: Total spills counted: " << spill_count << " Total spills removed : " << spills_removed << std::endl; + + return spill_count; +} + +void sbn::ICARUSBNBRetriever::beginSubRun(art::SubRun& sr) +{ + return; +} + +//____________________________________________________________________________ +void sbn::ICARUSBNBRetriever::endSubRun(art::SubRun& sr) +{ + // We will add all of the BNBSpillInfo data-products to the + // art::SubRun so it persists + // currently this is ~2.7 kB/event or ~0.07 kB/spill + +mf::LogDebug("ICARUSBNBRetriever")<< "Total number of DAQ Spills : " << TotalBeamSpills << std::endl; +mf::LogDebug("ICARUSBNBRetriever")<< "Total number of Selected Spills : " << fOutbeamInfos.size() << std::endl; + + auto p = std::make_unique< std::vector< sbn::BNBSpillInfo > >(); + std::swap(*p, fOutbeamInfos); + + sr.put(std::move(p), art::subRunFragment()); + + return; +} + +DEFINE_ART_MODULE(sbn::ICARUSBNBRetriever) diff --git a/sbncode/BeamSpillInfoRetriever/NuMIEXTRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/NuMIEXTRetriever/CMakeLists.txt index f11657b00..e323d253b 100644 --- a/sbncode/BeamSpillInfoRetriever/NuMIEXTRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/NuMIEXTRetriever/CMakeLists.txt @@ -1,6 +1,6 @@ cet_build_plugin(NuMIEXTRetriever art::module - LIBRARIES + LIBRARIES PRIVATE art::Persistency_Common art::Utilities canvas::canvas cetlib::cetlib cetlib_except::cetlib_except diff --git a/sbncode/BeamSpillInfoRetriever/NuMIRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/NuMIRetriever/CMakeLists.txt index d5fd24b7c..5f03fb618 100644 --- a/sbncode/BeamSpillInfoRetriever/NuMIRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/NuMIRetriever/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(ifbeam) find_package(ifdh_art) cet_build_plugin(NuMIRetriever art::module - LIBRARIES + LIBRARIES PRIVATE art::Persistency_Common art::Utilities canvas::canvas cetlib::cetlib cetlib_except::cetlib_except diff --git a/sbncode/BeamSpillInfoRetriever/SBNDPOTTools.cpp b/sbncode/BeamSpillInfoRetriever/POTTools.cpp similarity index 59% rename from sbncode/BeamSpillInfoRetriever/SBNDPOTTools.cpp rename to sbncode/BeamSpillInfoRetriever/POTTools.cpp index 9acb26ab2..e43dce397 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDPOTTools.cpp +++ b/sbncode/BeamSpillInfoRetriever/POTTools.cpp @@ -1,12 +1,10 @@ -#include "SBNDPOTTools.h" +#include "POTTools.h" using namespace std; -namespace sbn{ +namespace sbn::pot{ - // Function for extracting the PTB information (current timestamp, - // previous timestamp, gate counter). Used in SBND modules only. - sbn::PTBInfo_t extractPTBInfo(art::Handle > cont_frags, int HLT) { + sbn::pot::PTBInfo_t extractPTBInfo(art::Handle > cont_frags, int HLT) { bool foundHLT = false; PTBInfo_t PTBInfo; for (auto const& cont : *cont_frags) @@ -22,11 +20,11 @@ namespace sbn{ if (ctb_frag.Trigger(word_i)->IsHLT() && ctb_frag.Trigger(word_i)->IsTrigger(HLT)) { foundHLT = true; - uint64_t RawprevPTBTimeStamp = ctb_frag.PTBWord(word_i)->prevTS * 20; - uint64_t RawcurrPTBTimeStamp = ctb_frag.Trigger(word_i)->timestamp * 20; - double currTS_candidate = std::bitset<64>(RawcurrPTBTimeStamp/20).to_ullong()/50e6; + uint64_t RawprevPTBTimeStamp = ctb_frag.PTBWord(word_i)->prevTS; + uint64_t RawcurrPTBTimeStamp = ctb_frag.Trigger(word_i)->timestamp; + std::uint64_t currTS_candidate = std::bitset<64>(RawcurrPTBTimeStamp).to_ullong() * 20; if(currTS_candidate < PTBInfo.currPTBTimeStamp){ - PTBInfo.prevPTBTimeStamp = std::bitset<64>(RawprevPTBTimeStamp / 20).to_ullong()/50e6; + PTBInfo.prevPTBTimeStamp = std::bitset<64>(RawprevPTBTimeStamp).to_ullong() * 20; PTBInfo.currPTBTimeStamp = currTS_candidate; PTBInfo.GateCounter = ctb_frag.Trigger(word_i)->gate_counter; } @@ -45,7 +43,35 @@ namespace sbn{ } } - // Function for extracting the TDC timestamp. Used in SBND modules only. + std::vector extractAllPTBInfo(std::vector const& cont_frags) { + std::vector PTBInfoVec; + for (auto const& cont : cont_frags) + { + artdaq::ContainerFragment cont_frag(cont); + for (size_t fragi = 0; fragi < cont_frag.block_count(); ++fragi) + { + artdaq::Fragment frag = *cont_frag[fragi]; + sbndaq::CTBFragment ctb_frag(frag); + for(size_t word_i = 0; word_i < ctb_frag.NWords(); ++word_i) + { + if(ctb_frag.Trigger(word_i)){ + PTBInfo_t PTBInfo; + uint64_t RawprevPTBTimeStamp = ctb_frag.PTBWord(word_i)->prevTS; + uint64_t RawcurrPTBTimeStamp = ctb_frag.Trigger(word_i)->timestamp; + PTBInfo.prevPTBTimeStamp = std::bitset<64>(RawprevPTBTimeStamp).to_ullong() * 20; + PTBInfo.currPTBTimeStamp = std::bitset<64>(RawcurrPTBTimeStamp).to_ullong() * 20; + PTBInfo.GateCounter = ctb_frag.Trigger(word_i)->gate_counter; + PTBInfo.isHLT = ctb_frag.Trigger(word_i)->IsHLT(); + PTBInfo.triggerWord = ctb_frag.Trigger(word_i)->trigger_word; + PTBInfoVec.push_back(PTBInfo); + } + } + } + } + + return PTBInfoVec; + } + double extractTDCTimeStamp(art::Handle > cont_frags) { double TDCTimeStamp = 0; @@ -62,10 +88,8 @@ namespace sbn{ return TDCTimeStamp; } - // Collect device information to make BNBSpillInfo object. Used in - // ICARUS and SBND modules. sbn::BNBSpillInfo makeBNBSpillInfo - (art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR, std::unique_ptr const& bfp) + (art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR, std::unique_ptr const& bfp, const std::unique_ptr & offsets,const std::unique_ptr & vp873) { auto const& [ MWR_times, unpacked_MWR ] = MWRdata; // alias @@ -73,41 +97,81 @@ namespace sbn{ // initializing all of our device carriers // device definitions can be found in BNBSpillInfo.h - double TOR860 = 0; // units e12 protons - double TOR875 = 0; // units e12 protons - double LM875A = 0; // units R/s - double LM875B = 0; // units R/s - double LM875C = 0; // units R/s - double HP875 = 0; // units mm - double VP875 = 0; // units mm - double HPTG1 = 0; // units mm - double VPTG1 = 0; // units mm - double HPTG2 = 0; // units mm - double VPTG2 = 0; // units mm - double BTJT2 = 0; // units Deg C - double THCURR = 0; // units kiloAmps - - double TOR860_time = 0; // units s + double TOR860 = -999; // units e12 protons + double TOR875 = -999; // units e12 protons + double LM875A = -999; // units R/s + double LM875B = -999; // units R/s + double LM875C = -999; // units R/s + double HP873 = -999; // units mm + double VP873 = -999; // units mm; not in the first IFBeam query bunch + double HP875 = -999; // units mm + double VP875 = -999; // units mm + double HPTG1 = -999; // units mm + double VPTG1 = -999; // units mm + double HPTG2 = -999; // units mm + double VPTG2 = -999; // units mm + double BTJT2 = -999; // units Deg C + double THCURR = -999; // units kiloAmps + double M875HS = -999; // units mm + double M875VS = -999; // units mm + double M875HM = -999; // units mm + double M875VM = -999; // units mm + double M876HS = -999; // units mm + double M876VS = -999; // units mm + double M876HM = -999; // units mm + double M876VM = -999; // units mm + + double HP875Offset =-999;//units mm; make a another separate IFBeam query bunch for offsets + double VP875Offset =-999;//units mm + double VP873Offset =-999;//units mm + double HPTG1Offset =-999;//units mm + double HPTG2Offset =-999;//units mm + double VPTG1Offset =-999;//units mm + double VPTG2Offset =-999;//units mm + + double TOR860_time = -999; // units s + + double FOM =-999; + // Here we request all the devices // since sometimes devices fail to report we'll // allow each to throw an exception but still move forward // interpreting these failures will be part of the beam quality analyses - - try{bfp->GetNamedData(time, "E:TOR860@",&TOR860,&TOR860_time);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:TOR875",&TOR875);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875A",&LM875A);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875B",&LM875B);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:LM875C",&LM875C);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HP875",&HP875);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VP875",&VP875);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HPTG1",&HPTG1);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VPTG1",&VPTG1);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:HPTG2",&HPTG2);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:VPTG2",&VPTG2);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:BTJT2",&BTJT2);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - try{bfp->GetNamedData(time, "E:THCURR",&THCURR);}catch (WebAPIException &we) {mf::LogDebug("SBNDBNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} - + + try{bfp->GetNamedData(time, "E:TOR860@",&TOR860,&TOR860_time);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:TOR875",&TOR875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:LM875A",&LM875A);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:LM875B",&LM875B);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:LM875C",&LM875C);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:HP873",&HP873);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{vp873->GetNamedData(time, "E:VP873",&VP873);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:HP875",&HP875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:VP875",&VP875);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:HPTG1",&HPTG1);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:VPTG1",&VPTG1);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:HPTG2",&HPTG2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:VPTG2",&VPTG2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:BTJT2",&BTJT2);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:THCURR",&THCURR);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + + try{bfp->GetNamedData(time, "E:M875HS",&M875HS);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M875VS",&M875VS);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M875HM",&M875HM);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M875VM",&M875VM);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M876HS",&M876HS);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M876VS",&M876VS);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M876HM",&M876HM);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{bfp->GetNamedData(time, "E:M876VM",&M876VM);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + + try{offsets->GetNamedData(time, "E_VP873S",&VP873Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_HP875S",&HP875Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_VP875S",&VP875Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_HPTG1S",&HPTG1Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_HPTG2S",&HPTG2Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_VPTG1S",&VPTG1Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + try{offsets->GetNamedData(time, "E_VPTG2S",&VPTG2Offset);}catch (WebAPIException &we) {mf::LogDebug("BNBRetriever")<< "At time : " << time << " " << "got exception: " << we.what() << "\n";} + //crunch the times unsigned long int time_closest_int = (int) TOR860_time; double time_closest_ns = (TOR860_time - time_closest_int)*1e9; @@ -119,6 +183,8 @@ namespace sbn{ beamInfo.LM875A = LM875A; beamInfo.LM875B = LM875B; beamInfo.LM875C = LM875C; + beamInfo.HP873 = HP873; + beamInfo.VP873 = VP873; beamInfo.HP875 = HP875; beamInfo.VP875 = VP875; beamInfo.HPTG1 = HPTG1; @@ -127,9 +193,27 @@ namespace sbn{ beamInfo.VPTG2 = VPTG2; beamInfo.BTJT2 = BTJT2; beamInfo.THCURR = THCURR; + beamInfo.M875HS = M875HS; + beamInfo.M875VS = M875VS; + beamInfo.M875HM = M875HM; + beamInfo.M875VM = M875VM; + beamInfo.M876HS = M876HS; + beamInfo.M876VS = M876VS; + beamInfo.M876HM = M876HM; + beamInfo.M876VM = M876VM; beamInfo.spill_time_s = time_closest_int; beamInfo.spill_time_ns = time_closest_ns; - + beamInfo.VP873 = VP873; + beamInfo.VP873Offset = VP873Offset; + beamInfo.HP875Offset = HP875Offset; + beamInfo.VP875Offset = VP875Offset; + beamInfo.HPTG1Offset = HPTG1Offset; + beamInfo.HPTG2Offset = HPTG2Offset; + beamInfo.VPTG1Offset = VPTG1Offset; + beamInfo.VPTG2Offset = VPTG2Offset; + beamInfo.FOM = FOM; + + for(auto const& MWRdata: unpacked_MWR){ std::ignore = MWRdata; assert(!MWRdata.empty()); @@ -166,30 +250,31 @@ namespace sbn{ // information, so we'll write it to the SubRun beamInfo.event = eventID.event(); // the rest of ID is known by art::SubRun - + + //for(size_t m=0; m< beamInfo.M875BB.size(); m++){ + //std::cout << "M875BB: " < const& bfp) { - double TOR860 = 0; // units e12 protons - double TOR875 = 0; // units e12 protons - double LM875A = 0; // units R/s - double LM875B = 0; // units R/s - double LM875C = 0; // units R/s - double HP875 = 0; // units mm - double VP875 = 0; // units mm - double HPTG1 = 0; // units mm - double VPTG1 = 0; // units mm - double HPTG2 = 0; // units mm - double VPTG2 = 0; // units mm - double BTJT2 = 0; // units Deg C - double THCURR = 0; // units kiloAmps + double TOR860 = -999; // units e12 protons + double TOR875 = -999; // units e12 protons + double LM875A = -999; // units R/s + double LM875B = -999; // units R/s + double LM875C = -999; // units R/s + double HP875 = -999; // units mm + double VP875 = -999; // units mm + double HPTG1 = -999; // units mm + double VPTG1 = -999; // units mm + double HPTG2 = -999; // units mm + double VPTG2 = -999; // units mm + double BTJT2 = -999; // units Deg C + double THCURR = -999; // units kiloAmps - double TOR860_time = 0; // units s + double TOR860_time = -999; // units s // Here we request all the devices // since sometimes devices fail to report we'll @@ -221,8 +306,7 @@ namespace sbn{ } } - // extract spill times from the IFBeam data base. Used in SBND and ICARUS. - sbn::MWRdata_t extractSpillTimes(TriggerInfo_t const& triggerInfo, std::unique_ptr const& bfp, std::unique_ptr const& bfp_mwr, double fTimePad, double MWRtoroidDelay, sbn::MWRData mwrdata) { + sbn::pot::MWRdata_t extractSpillTimes(TriggerInfo_t const& triggerInfo, std::unique_ptr const& bfp, std::unique_ptr const& bfp_mwr, double fTimePad, double MWRtoroidDelay, sbn::MWRData mwrdata) { // These lines get everything primed within the IFBeamDB. try{bfp->FillCache((triggerInfo.t_current_event)+fTimePad);} catch (WebAPIException &we) {}; @@ -331,4 +415,8 @@ namespace sbn{ return { std::move(MWR_times), std::move(unpacked_MWR) }; } + } + + + diff --git a/sbncode/BeamSpillInfoRetriever/POTTools.h b/sbncode/BeamSpillInfoRetriever/POTTools.h new file mode 100644 index 000000000..1e54647a8 --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/POTTools.h @@ -0,0 +1,92 @@ +#ifndef _SBNDPOTTOOLS_H +#define _SBNDPOTTOOLS_H + +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "ifdh_art/IFBeamService/IFBeam_service.h" + +#include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" +#include "sbndaq-artdaq-core/Overlays/SBND/PTBFragment.hh" +#include "sbndaq-artdaq-core/Overlays/SBND/TDCTimestampFragment.hh" +#include "artdaq-core/Data/ContainerFragment.hh" + +#include "sbncode/BeamSpillInfoRetriever/MWRData.h" +#include "larcorealg/CoreUtils/counter.h" +#include + +namespace sbn::pot{ + + typedef struct PTBInfo_t { + std::uint64_t currPTBTimeStamp = UINT64_MAX; ///< Timestamp in UTC nanoseconds since Unix epoch (converted from 20ns clock ticks) + std::uint64_t prevPTBTimeStamp = 0; + unsigned int GateCounter = 0; + bool isHLT = false; + uint64_t triggerWord = 0; ///< Timestamp in s since beam extraction signal + } PTBInfo_t; + + typedef struct TriggerInfo_t { + int gate_type = 0; ///< Source of the spill: `1`: BNB, `2`: NuMI + double t_current_event = 0; ///< Timestamp in UTC seconds since Unix epoch (converted from 20ns clock ticks) + double t_previous_event = 0; + unsigned int number_of_gates_since_previous_event = 0; + std::int64_t WR_to_Spill_conversion = 0; + } TriggerInfo_t; + + typedef struct MWRdata_t { + std::vector< std::vector > MWR_times; + std::vector< std::vector< std::vector< int > > > unpacked_MWR; + } MWRdata_t; + + /** + * @brief Extracts information from PTB for a single HLT for use in SBND POT accounting. + * + * @param cont_frags The PTB fragments to examine. + * @param HLT The high level trigger we are searching for. + */ + PTBInfo_t extractPTBInfo(art::Handle > cont_frags, int HLT); + + /** + * @brief Extracts ALL PTB information for using in SBND CAF files. + * + * @param cont_frags The PTB fragments to examine. + * @return Vector of PTBInfo_t containing all triggers found. + */ + std::vector extractAllPTBInfo(std::vector const& cont_frags); + + /** + * @brief Extracts information from TDC for use in SBND POT accounting. + * + * @param cont_frags The TDC fragments to examine. + */ + double extractTDCTimeStamp(art::Handle > cont_frags); + + /** + * @brief Checks for a known IFBeam issue where TOR clocks are incorrect. + * @return true if a time in IFBeam corresponds to a broken clock false if + * it is a real spill. + * @details Check validity by seeing if all devices fire at once or + * if time is just a single outlier. + * @param time The time for which we want to check for a spill. + * @param bfp beamfolder object to check. + */ + bool BrokenClock(double time, std::unique_ptr const& bfp); + + /** + * @brief extract spill times from the IFBeam data base. Used in SBND and ICARUS. + * + * @param triggerInfo triggerInfo object to match + * @param bfp beamfolder to match + * @param bfp_mwr mwr devices have separate daq system, much treat separately + * @param fTimePad Padding to use in ifbeam queries + * @param MWRtoroidDelay Delay between MWR and toroids + * @param mwrdata MWRData to unpack + */ + MWRdata_t extractSpillTimes(TriggerInfo_t const& triggerInfo, std::unique_ptr const& bfp, std::unique_ptr const& bfp_mwr, double fTimePad, double MWRtoroidDelay, sbn::MWRData mwrdata ); + /** + * @brief Compile spill information into BNBSpillInfo object + */ + sbn::BNBSpillInfo makeBNBSpillInfo(art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR, std::unique_ptr const& bfp, std::unique_ptr const& offsets, std::unique_ptr const& vp873); +} + +#endif diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/CMakeLists.txt index d7f43205a..ff9aee2b3 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/CMakeLists.txt @@ -2,8 +2,8 @@ find_package(ifbeam) find_package(ifdh_art) cet_build_plugin(SBNDBNBEXTRetriever art::module - LIBRARIES - sbn_SBNDPOTTools + LIBRARIES PRIVATE + sbn_POTTools ) install_headers() diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/SBNDBNBEXTRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/SBNDBNBEXTRetriever_module.cc index 298734bff..a8b3f9d67 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/SBNDBNBEXTRetriever_module.cc +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBEXTRetriever/SBNDBNBEXTRetriever_module.cc @@ -8,7 +8,7 @@ #include "art/Framework/Core/EDProducer.h" #include "messagefacility/MessageLogger/MessageLogger.h" #include "sbnobj/Common/POTAccounting/EXTCountInfo.h" -#include "sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" namespace sbn { class SBNDBNBEXTRetriever; @@ -28,11 +28,10 @@ class sbn::SBNDBNBEXTRetriever : public art::EDProducer { SBNDBNBEXTRetriever & operator = (SBNDBNBEXTRetriever const &) = delete; SBNDBNBEXTRetriever & operator = (SBNDBNBEXTRetriever &&) = delete; - private: // Declare member data here. std::vector< sbn::EXTCountInfo > fOutExtInfos; - TriggerInfo_t extractTriggerInfo(art::Event const& e) const; + sbn::pot::TriggerInfo_t extractTriggerInfo(art::Event const& e) const; // input labels float TotalEXTCounts; }; @@ -47,7 +46,7 @@ void sbn::SBNDBNBEXTRetriever::produce(art::Event & e) { art::InputTag PTB_itag("daq", "ContainerPTB"); auto PTB_cont_frags = e.getHandle(PTB_itag); - PTBInfo_t PTBInfo = extractPTBInfo(PTB_cont_frags, 4); + sbn::pot::PTBInfo_t PTBInfo = sbn::pot::extractPTBInfo(PTB_cont_frags, 4); int SingleEventGateCounter = PTBInfo.GateCounter; TotalEXTCounts += SingleEventGateCounter; diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/CMakeLists.txt index ea89ae916..a07fab958 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/CMakeLists.txt @@ -2,8 +2,9 @@ find_package(ifbeam) find_package(ifdh_art) cet_build_plugin(SBNDBNBRetriever art::module - LIBRARIES - sbn_SBNDPOTTools + LIBRARIES PRIVATE + sbn_POTTools + sbn_getFOM ) install_headers() diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/SBNDBNBRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/SBNDBNBRetriever_module.cc index 5e709c2b0..e6465d45b 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/SBNDBNBRetriever_module.cc +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBRetriever/SBNDBNBRetriever_module.cc @@ -8,7 +8,8 @@ #include "art/Framework/Core/EDProducer.h" #include "messagefacility/MessageLogger/MessageLogger.h" #include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" -#include "sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" +#include "sbncode/BeamSpillInfoRetriever/getFOM.h" namespace sbn { class SBNDBNBRetriever; @@ -35,16 +36,19 @@ class sbn::SBNDBNBRetriever : public art::EDProducer { double fBESOffset; std::string fDeviceUsedForTiming; std::unique_ptr bfp; + std::unique_ptr offsets; + std::unique_ptr vp873; std::unique_ptr bfp_mwr; + sbn::MWRData mwrdata; art::ServiceHandle ifbeam_handle; static constexpr double MWRtoroidDelay = -0.035; ///< the same time point is measured _t_ by MWR and _t + MWRtoroidDelay`_ by the toroid [ms] - TriggerInfo_t extractTriggerInfo(art::Event const& e) const; + sbn::pot::TriggerInfo_t extractTriggerInfo(art::Event const& e) const; int matchMultiWireData( art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, std::vector< sbn::BNBSpillInfo >& beamInfos ) const; unsigned int TotalBeamSpills; @@ -56,12 +60,19 @@ sbn::SBNDBNBRetriever::SBNDBNBRetriever(fhicl::ParameterSet const & params) fTimePad = params.get("TimePadding"); fBESOffset = params.get("BESOffset"); fDeviceUsedForTiming = params.get("DeviceUsedForTiming"); - bfp = ifbeam_handle->getBeamFolder(params.get("Bundle"), params.get("URL"), std::stod(params.get("TimeWindow"))); + double const timeWindow = std::stod(params.get("TimeWindow")); + bfp = ifbeam_handle->getBeamFolder(params.get("Bundle"), params.get("URL"), timeWindow); bfp->set_epsilon(0.02); bfp_mwr = ifbeam_handle->getBeamFolder(params.get("MultiWireBundle"), params.get("URL"), std::stod(params.get("MWR_TimeWindow"))); bfp_mwr->set_epsilon(0.5); bfp_mwr->setValidWindow(3605); TotalBeamSpills = 0; + vp873 = ifbeam_handle->getBeamFolder(params.get("VP873Bundle"), params.get("URL"), timeWindow); + vp873->set_epsilon(0.02); + + offsets = ifbeam_handle->getBeamFolder(params.get("OffsetBundle"), params.get("URL"), timeWindow); + offsets->set_epsilon(1000); + } void sbn::SBNDBNBRetriever::produce(art::Event & e) @@ -77,14 +88,14 @@ void sbn::SBNDBNBRetriever::produce(art::Event & e) } mf::LogDebug("SBNDBNBRetriever")<< "ptb_event: " << e.event() << std::endl; - TriggerInfo_t const triggerInfo = extractTriggerInfo(e); + sbn::pot::TriggerInfo_t const triggerInfo = extractTriggerInfo(e); if (triggerInfo.t_previous_event == 0) { return; } TotalBeamSpills += triggerInfo.number_of_gates_since_previous_event; - MWRdata_t const MWRdata = extractSpillTimes(triggerInfo, bfp, bfp_mwr, fTimePad, MWRtoroidDelay, mwrdata); + sbn::pot::MWRdata_t const MWRdata = extractSpillTimes(triggerInfo, bfp, bfp_mwr, fTimePad, MWRtoroidDelay, mwrdata); int const spill_count = matchMultiWireData(e.id(), triggerInfo, MWRdata, fOutbeamInfos); @@ -94,7 +105,7 @@ void sbn::SBNDBNBRetriever::produce(art::Event & e) mf::LogDebug("SBNDBNBRetriever")<< "Event Spills : " << spill_count << ", DAQ Spills : " << triggerInfo.number_of_gates_since_previous_event << std::endl; } -sbn::TriggerInfo_t sbn::SBNDBNBRetriever::extractTriggerInfo(art::Event const& e) const { +sbn::pot::TriggerInfo_t sbn::SBNDBNBRetriever::extractTriggerInfo(art::Event const& e) const { // Using TDC for current event, but PTB for previous event. Exception for case where no TDC. art::InputTag PTB_itag("daq", "ContainerPTB"); auto PTB_cont_frags = e.getHandle(PTB_itag); @@ -102,23 +113,23 @@ sbn::TriggerInfo_t sbn::SBNDBNBRetriever::extractTriggerInfo(art::Event const& e art::InputTag TDC_itag("daq", "ContainerTDCTIMESTAMP"); auto TDC_cont_frags = e.getHandle(TDC_itag); - TriggerInfo_t triggerInfo; - PTBInfo_t PTBInfo = extractPTBInfo(PTB_cont_frags, 2); + sbn::pot::TriggerInfo_t triggerInfo; + sbn::pot::PTBInfo_t PTBInfo = sbn::pot::extractPTBInfo(PTB_cont_frags, 2); if (TDC_cont_frags) { - double TDCTimeStamp = extractTDCTimeStamp(TDC_cont_frags); + double TDCTimeStamp = sbn::pot::extractTDCTimeStamp(TDC_cont_frags); triggerInfo.t_current_event = TDCTimeStamp - fBESOffset; } else{ // If missing TDC, use PTB instead mf::LogDebug("SBNDBNBRetriever") << " Missing TDC Container Fragments!!! " << std::endl; - triggerInfo.t_current_event = PTBInfo.currPTBTimeStamp - fBESOffset; + triggerInfo.t_current_event = PTBInfo.currPTBTimeStamp / 1e9 - fBESOffset; } - triggerInfo.t_previous_event = PTBInfo.prevPTBTimeStamp - fBESOffset; + triggerInfo.t_previous_event = PTBInfo.prevPTBTimeStamp / 1e9 - fBESOffset; triggerInfo.number_of_gates_since_previous_event = PTBInfo.GateCounter; - double PTBandCurrOffset = PTBInfo.currPTBTimeStamp - triggerInfo.t_current_event - fBESOffset; + double PTBandCurrOffset = PTBInfo.currPTBTimeStamp / 1e9 - triggerInfo.t_current_event - fBESOffset; // Catch for an issue seen a few times where PTB off by a second. // Only need to correct prevTS because either currTS is from TDC @@ -134,8 +145,8 @@ sbn::TriggerInfo_t sbn::SBNDBNBRetriever::extractTriggerInfo(art::Event const& e int sbn::SBNDBNBRetriever::matchMultiWireData( art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, std::vector< sbn::BNBSpillInfo >& beamInfos ) const { @@ -164,7 +175,7 @@ int sbn::SBNDBNBRetriever::matchMultiWireData( spills_removed++; continue;} - if(BrokenClock(times_temps[i], bfp)){ + if(sbn::pot::BrokenClock(times_temps[i], bfp)){ continue; } @@ -210,8 +221,11 @@ int sbn::SBNDBNBRetriever::matchMultiWireData( }//end loop over MWR devices - sbn::BNBSpillInfo spillInfo = makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR, bfp); + sbn::BNBSpillInfo spillInfo = makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR, bfp, offsets, vp873); + double const spillFOM = sbn::getBNBqualityFOM(spillInfo); + spillInfo.FOM = spillFOM; + beamInfos.push_back(std::move(spillInfo)); // We do not write these to the art::Events because diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/CMakeLists.txt b/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/CMakeLists.txt index d1ef9e65f..7187c8451 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/CMakeLists.txt +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/CMakeLists.txt @@ -2,8 +2,9 @@ find_package(ifbeam) find_package(ifdh_art) cet_build_plugin(SBNDBNBZEROBIASRetriever art::module - LIBRARIES - sbn_SBNDPOTTools + LIBRARIES PRIVATE + sbn_POTTools + sbn_getFOM ) install_headers() diff --git a/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/SBNDBNBZEROBIASRetriever_module.cc b/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/SBNDBNBZEROBIASRetriever_module.cc index fa7e8b793..298e0df91 100644 --- a/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/SBNDBNBZEROBIASRetriever_module.cc +++ b/sbncode/BeamSpillInfoRetriever/SBNDBNBZEROBIASRetriever/SBNDBNBZEROBIASRetriever_module.cc @@ -8,7 +8,8 @@ #include "art/Framework/Core/EDProducer.h" #include "messagefacility/MessageLogger/MessageLogger.h" #include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" -#include "sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" +#include "sbncode/BeamSpillInfoRetriever/getFOM.h" namespace sbn { class SBNDBNBZEROBIASRetriever; @@ -34,6 +35,8 @@ class sbn::SBNDBNBZEROBIASRetriever : public art::EDProducer { double fBESOffset; std::string fDeviceUsedForTiming; std::unique_ptr bfp; + std::unique_ptr offsets; + std::unique_ptr vp873; std::unique_ptr bfp_mwr; sbn::MWRData mwrdata; art::ServiceHandle ifbeam_handle; @@ -41,11 +44,11 @@ class sbn::SBNDBNBZEROBIASRetriever : public art::EDProducer { std::vector< sbn::BNBSpillInfo > fOutbeamInfos; std::vector< sbn::BNBSpillInfo > fOutbeamInfosTotal; - TriggerInfo_t extractTriggerInfo(art::Event const& e) const; + sbn::pot::TriggerInfo_t extractTriggerInfo(art::Event const& e) const; void matchMultiWireData( art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, std::vector< sbn::BNBSpillInfo >& beamInfos ) const; unsigned int TotalBeamSpills; @@ -56,11 +59,18 @@ sbn::SBNDBNBZEROBIASRetriever::SBNDBNBZEROBIASRetriever(fhicl::ParameterSet cons fTimePad = params.get("TimePadding"); fDeviceUsedForTiming = params.get("DeviceUsedForTiming"); fBESOffset = params.get("BESOffset"); - bfp = ifbeam_handle->getBeamFolder(params.get("Bundle"), params.get("URL"), std::stod(params.get("TimeWindow"))); + const double timeWindow = std::stod(params.get("TimeWindow")); + bfp = ifbeam_handle->getBeamFolder(params.get("Bundle"), params.get("URL"), timeWindow); bfp->set_epsilon(0.02); bfp_mwr = ifbeam_handle->getBeamFolder(params.get("MultiWireBundle"), params.get("URL"), std::stod(params.get("MWR_TimeWindow"))); bfp_mwr->set_epsilon(0.5); bfp_mwr->setValidWindow(3605); + vp873 = ifbeam_handle->getBeamFolder(params.get("VP873Bundle"), params.get("URL"), timeWindow); + vp873->set_epsilon(0.02); + + offsets = ifbeam_handle->getBeamFolder(params.get("OffsetBundle"), params.get("URL"), timeWindow); + offsets->set_epsilon(600); + TotalBeamSpills = 0; produces< std::vector< sbn::BNBSpillInfo >, art::InEvent >(); produces< std::vector< sbn::BNBSpillInfo >, art::InSubRun >(); @@ -79,7 +89,7 @@ void sbn::SBNDBNBZEROBIASRetriever::produce(art::Event & e) return; } - TriggerInfo_t const triggerInfo = extractTriggerInfo(e); + sbn::pot::TriggerInfo_t const triggerInfo = extractTriggerInfo(e); if (triggerInfo.t_previous_event == 0) { auto p = std::make_unique< std::vector< sbn::BNBSpillInfo > >(); @@ -89,7 +99,7 @@ void sbn::SBNDBNBZEROBIASRetriever::produce(art::Event & e) } TotalBeamSpills += triggerInfo.number_of_gates_since_previous_event; - MWRdata_t const MWRdata = extractSpillTimes(triggerInfo, bfp, bfp_mwr, fTimePad, MWRtoroidDelay, mwrdata); + sbn::pot::MWRdata_t const MWRdata = extractSpillTimes(triggerInfo, bfp, bfp_mwr, fTimePad, MWRtoroidDelay, mwrdata); matchMultiWireData(e.id(), triggerInfo, MWRdata, fOutbeamInfos); fOutbeamInfosTotal.insert(fOutbeamInfosTotal.end(),fOutbeamInfos.begin(),fOutbeamInfos.end()); @@ -98,7 +108,7 @@ void sbn::SBNDBNBZEROBIASRetriever::produce(art::Event & e) e.put(std::move(p)); } -sbn::TriggerInfo_t sbn::SBNDBNBZEROBIASRetriever::extractTriggerInfo(art::Event const& e) const { +sbn::pot::TriggerInfo_t sbn::SBNDBNBZEROBIASRetriever::extractTriggerInfo(art::Event const& e) const { // Using TDC for current event, but PTB for previous event art::InputTag PTB_itag("daq", "ContainerPTB"); auto PTB_cont_frags = e.getHandle(PTB_itag); @@ -106,23 +116,23 @@ sbn::TriggerInfo_t sbn::SBNDBNBZEROBIASRetriever::extractTriggerInfo(art::Event art::InputTag TDC_itag("daq", "ContainerTDCTIMESTAMP"); auto TDC_cont_frags = e.getHandle(TDC_itag); - TriggerInfo_t triggerInfo; - PTBInfo_t PTBInfo = extractPTBInfo(PTB_cont_frags, 1); + sbn::pot::TriggerInfo_t triggerInfo; + sbn::pot::PTBInfo_t PTBInfo = sbn::pot::extractPTBInfo(PTB_cont_frags, 1); if (TDC_cont_frags) { - double TDCTimeStamp = extractTDCTimeStamp(TDC_cont_frags); + double TDCTimeStamp = sbn::pot::extractTDCTimeStamp(TDC_cont_frags); triggerInfo.t_current_event = TDCTimeStamp - fBESOffset; } else{ // If missing TDC, use PTB instead mf::LogDebug("SBNDBNBZEROBIASRetriever") << " Missing TDC Container Fragments!!!" << std::endl; - triggerInfo.t_current_event = PTBInfo.currPTBTimeStamp - fBESOffset; + triggerInfo.t_current_event = PTBInfo.currPTBTimeStamp / 1e9 - fBESOffset; } - triggerInfo.t_previous_event = PTBInfo.prevPTBTimeStamp - fBESOffset; + triggerInfo.t_previous_event = PTBInfo.prevPTBTimeStamp / 1e9 - fBESOffset; triggerInfo.number_of_gates_since_previous_event = PTBInfo.GateCounter; - double PTBandCurrOffset = PTBInfo.currPTBTimeStamp - triggerInfo.t_current_event - fBESOffset; + double PTBandCurrOffset = PTBInfo.currPTBTimeStamp / 1e9 - triggerInfo.t_current_event - fBESOffset; // Catch for an issue seen a few times where PTB off by a second. // Only need to correct prevTS because either currTS is from TDC @@ -138,8 +148,8 @@ sbn::TriggerInfo_t sbn::SBNDBNBZEROBIASRetriever::extractTriggerInfo(art::Event void sbn::SBNDBNBZEROBIASRetriever::matchMultiWireData( art::EventID const& eventID, - TriggerInfo_t const& triggerInfo, - MWRdata_t const& MWRdata, + sbn::pot::TriggerInfo_t const& triggerInfo, + sbn::pot::MWRdata_t const& MWRdata, std::vector< sbn::BNBSpillInfo >& beamInfos ) const { @@ -174,7 +184,7 @@ void sbn::SBNDBNBZEROBIASRetriever::matchMultiWireData( spills_removed++; continue;} - if(BrokenClock(times_temps[i], bfp)){ + if(sbn::pot::BrokenClock(times_temps[i], bfp)){ continue; } @@ -217,7 +227,10 @@ void sbn::SBNDBNBZEROBIASRetriever::matchMultiWireData( }//end loop over MWR times }//end loop over MWR devices - sbn::BNBSpillInfo spillInfo = makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR, bfp); + sbn::BNBSpillInfo spillInfo = makeBNBSpillInfo(eventID, times_temps[i], MWRdata, matched_MWR, bfp, offsets, vp873); + double spillFOM = sbn::getBNBqualityFOM(spillInfo); + spillInfo.FOM = spillFOM; + beamInfos.push_back(std::move(spillInfo)); // We do not write these to the art::Events because diff --git a/sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h b/sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h deleted file mode 100644 index 46d16c709..000000000 --- a/sbncode/BeamSpillInfoRetriever/SBNDPOTTools.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _SBNDPOTTOOLS_H -#define _SBNDPOTTOOLS_H - -#include "art/Framework/Principal/Event.h" -#include "art/Framework/Principal/Handle.h" -#include "messagefacility/MessageLogger/MessageLogger.h" -#include "ifdh_art/IFBeamService/IFBeam_service.h" - -#include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" -#include "sbndaq-artdaq-core/Overlays/SBND/PTBFragment.hh" -#include "sbndaq-artdaq-core/Overlays/SBND/TDCTimestampFragment.hh" -#include "artdaq-core/Data/ContainerFragment.hh" - -#include "sbncode/BeamSpillInfoRetriever/MWRData.h" -#include "larcorealg/CoreUtils/counter.h" - -namespace sbn{ - typedef struct PTBInfo_t { - double currPTBTimeStamp = 1e20; - double prevPTBTimeStamp = 0; - unsigned int GateCounter = 0; - } PTBInfo_t; - - typedef struct TriggerInfo_t { - double t_current_event = 0; - double t_previous_event = 0; - unsigned int number_of_gates_since_previous_event = 0; - } TriggerInfo_t; - - typedef struct MWRdata_t { - std::vector< std::vector > MWR_times; - std::vector< std::vector< std::vector< int > > > unpacked_MWR; - } MWRdata_t; - - PTBInfo_t extractPTBInfo(art::Handle > cont_frags, int HLT); - double extractTDCTimeStamp(art::Handle > cont_frags); - bool BrokenClock(double time, std::unique_ptr const& bfp); - MWRdata_t extractSpillTimes(TriggerInfo_t const& triggerInfo, std::unique_ptr const& bfp, std::unique_ptr const& bfp_mwr, double fTimePad, double MWRtoroidDelay, sbn::MWRData mwrdata ); - sbn::BNBSpillInfo makeBNBSpillInfo(art::EventID const& eventID, double time, MWRdata_t const& MWRdata, std::vector const& matched_MWR, std::unique_ptr const& bfp); -} - -#endif diff --git a/sbncode/BeamSpillInfoRetriever/getFOM.cpp b/sbncode/BeamSpillInfoRetriever/getFOM.cpp new file mode 100644 index 000000000..dfd91a02e --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/getFOM.cpp @@ -0,0 +1,419 @@ +/** + * @file sbncode/BeamSpillInfoRetriever/getFOM.cpp + * @brief Figure of Merit for BNB Spills using SBND information adapted from MicroBooNE FOM + * @author Max Dubnowski (maxdub@sas.upenn.edu or `@Max Dubnowski` on SBN Slack) + */ +#include "sbncode/BeamSpillInfoRetriever/getFOM.h" +#include +#include "TH1D.h" +#include "TFitResult.h" +#include + + +using namespace std; + + +namespace sbn { + + bool onePlot = true; + + float getBNBqualityFOM(BNBSpillInfo & spill ) + { + double fom=0; + + double hp875_offset= spill.HP875Offset; + double vp875_offset= spill.VP875Offset; + double vp873_offset= spill.VP873Offset; + double hptg1_offset= spill.HPTG1Offset; + //double vptg1_offset= spill.VPTG1Offset; + double hptg2_offset= spill.HPTG2Offset; + double vptg2_offset= spill.VPTG2Offset; + + //Decides which position monitor to check first + int useHTG = 1; + int useVTG = 1; + + //Z Position of the monitors in m + double const hp875_zpos= 202.116104; + double const vp875_zpos= 202.3193205; + double const vp873_zpos= 191.153656; + double const hptg1_zpos= 204.833267; + //double const vptg1_zpos= 204.629608; + double const hptg2_zpos= 205.240662; + double const vptg2_zpos= 205.036835; + double const target_center_zpos= 206.870895; + double const p875x[]={0.431857, 0.158077, 0.00303551}; + double const p875y[]={0.279128, 0.337048, 0}; + double const p876x[]={0.166172, 0.30999, -0.00630299}; + double const p876y[]={0.13425, 0.580862, 0}; + + + std::vector tor860; + std::vector tor875; + std::vector hp875; + std::vector vp875; + std::vector vp873; + std::vector hptg1; + std::vector vptg1; + std::vector hptg2; + std::vector vptg2; + + std::vector mw875(spill.M875BB.begin(), spill.M875BB.end()); + std::vector mw876(spill.M876BB.begin(), spill.M876BB.end()); + std::vector mwtgt(spill.MMBTBB.begin(), spill.MMBTBB.end()); + + + tor860.push_back(spill.TOR860); + tor875.push_back(spill.TOR875); + hp875.push_back(spill.HP875); + vp875.push_back(spill.VP875); + vp873.push_back(spill.VP873); + hptg1.push_back(spill.HPTG1); + vptg1.push_back(spill.VPTG1); + hptg2.push_back(spill.HPTG2); + vptg2.push_back(spill.VPTG2); + + double tor; + if (!tor860.empty()) + tor=tor860[0]; + else if (!tor875.empty()) + tor=tor875[0]; + else + return -1; + + /** + * @brief when creating ntuples for pot counting script the variables are filled with -999 + * this could create a difference when passing events with FOM>1 since + * events with missing BPM data would get FOM=2, while events with BPM set to -999 will get FOM=0 + * bad or missing MWR data gets FOM 4 in either case + */ + + if (hptg2.empty()) hptg2.push_back(-999); + if (hptg1.empty()) hptg1.push_back(-999); + if (hp875.empty()) hp875.push_back(-999); + if (vptg2.empty()) vptg2.push_back(-999); + if (vptg1.empty()) vptg1.push_back(-999); + if (vp875.empty()) vp875.push_back(-999); + if (vp873.empty()) vp873.push_back(-999); + double horang; + + auto interpolate_hp875 = [delta_hp875=(hp875[0]-hp875_offset), hp875_zpos, target_center_zpos] + (double delta_value, double zpos) + { + double const ang = (delta_value-delta_hp875)/(zpos-hp875_zpos); + double const pos = delta_hp875+ang*(target_center_zpos-hp875_zpos); + return std::pair(ang, pos); + }; + + // return 2 when missing essential beam horizontal position data: + if (hp875.empty() || (hptg1.empty() && hptg2.empty())) return 2; + bool const doUseHTG1 = (useHTG == 1) || hptg2.empty(); + auto const [ Tanhorang, horpos ] = doUseHTG1? + interpolate_hp875(hptg1[0] - hptg1_offset, hptg1_zpos): + interpolate_hp875(hptg2[0] - hptg2_offset, hptg2_zpos); + + + double verang; + auto interpolate_vp875 = [delta_vp875=(vp875[0]-vp875_offset), vp875_zpos, target_center_zpos] + (double delta_value, double zpos) + { + double const ang = (delta_value-delta_vp875)/(zpos-vp875_zpos); + double const pos = delta_vp875+ang*(target_center_zpos-vp875_zpos); + return std::pair(ang, pos); + }; + + // return 2 when missing essential beam horizontal position data: + if (vp875.empty() || (vptg1.empty() && vptg2.empty())) return 3; + bool const doUseVTG1 = (useVTG == 1) || vptg2.empty(); + auto const [ Tanverang, verpos ] = doUseVTG1? + interpolate_vp875(vp873[0] - vp873_offset, vp873_zpos): + interpolate_vp875(vptg2[0] - vptg2_offset, vptg2_zpos); + + horang=atan(Tanhorang); + verang=atan(Tanverang); + double xx,yy,sx,sy,chi2x,chi2y; + double tgtsx, tgtsy; + bool good_tgt=false; + bool good_876=false; + bool good_875=false; + constexpr size_t FirstXMWtgt =0; + constexpr size_t FirstYMWtgt =48; + const double smallSigmaX =0.5, largeSigmaX = 10, smallSigmaY = 0.3, largeSigmaY =10, maxChi2X = 20, maxChi2Y = 20; + if (mwtgt.size()>0) { + processBNBprofile(&mwtgt[FirstXMWtgt], xx, sx,chi2x); + processBNBprofile(&mwtgt[FirstYMWtgt], yy, sy, chi2y); + if (sx>smallSigmaX && sxsmallSigmaY && sy0) { + processBNBprofile(&mw876[FirstXMWtgt], xx,sx,chi2x); + processBNBprofile(&mw876[FirstYMWtgt], yy,sy,chi2y); + double tgtsx876=p876x[0]+p876x[1]*sx+p876x[2]*sx*sx; + double tgtsy876=p876y[0]+p876y[1]*sy+p876y[2]*sy*sy; + if (tgtsx876>smallSigmaX && tgtsx876smallSigmaY && tgtsy8760){ + processBNBprofile(&mw875[FirstXMWtgt], xx,sx,chi2x); + processBNBprofile(&mw875[FirstYMWtgt], yy,sy,chi2y); + double tgtsx875=p875x[0]+p875x[1]*sx+p875x[2]*sx*sx; + double tgtsy875=p875y[0]+p875y[1]*sy+p875y[2]*sy*sy; + if (tgtsx875>smallSigmaX && tgtsx875smallSigmaY && tgtsy875SetBinContent(i+1,-mwdata[i]-minx); + if (-mwdata[i]-minx > threshold && first_x==-1) first_x=i; + if (-mwdata[i]-minx > threshold) last_x=i+1; + hProf->SetBinError(i+1,error); + } + if (hProf->GetSumOfWeights()>0) { + TFitResultPtr const fit = hProf->Fit("gaus","QNS","",-12+first_x*0.5,-12+last_x*0.5); + x = fit->Parameter(1); + sx = fit->Parameter(2); + chi2= fit->Chi2() / fit->Ndf(); + delete hProf; + } else { + x=99999; + sx=99999; + chi2=99999; + } + entry += 1; + } + + + double calcFOM(double horpos, double horang, double verpos, double verang, double ppp, double tgtsx, double tgtsy) + { + ppp /= 1e12; //converts to 10^12 POT + + + //code from MiniBooNE AnalysisFramework with the addition of scaling the beam profile to match tgtsx, tgtsy + //form DQ_BeamLine_twiss_init.F + double bx = 4.68; + double ax = 0.0389; + double gx = (1+ax*ax)/bx; + double nx = 0.0958; + double npx = -0.0286; + double by = 59.12; + double ay = 2.4159; + double gy = (1+ay*ay)/by; + double ny = 0.4577; + double npy = -0.0271; + //from DQ_BeamLine_make_tgt_fom2.F + double ex = 0.1775E-06 + 0.1827E-07*ppp; + double ey = 0.1382E-06 + 0.2608E-08*ppp; + double dp = 0.4485E-03 + 0.6100E-04*ppp; + double tex = ex; + double tey = ey; + double tdp = dp; + //from DQ_BeamLine_beam_init.F + double sigma1[6][6]={{0}}; + double centroid1[6]={0}; + centroid1[0] = horpos; + centroid1[1] = horang; + centroid1[2] = verpos; + centroid1[3] = verang; + centroid1[4] = 0.0; + centroid1[5] = 0.0; + sigma1[5][5] = tdp*tdp; + sigma1[0][0] = tex*bx+ nx*nx *tdp*tdp; + sigma1[0][1] = -tex*ax+ nx*npx*tdp*tdp; + sigma1[1][1] = tex*gx+npx*npx*tdp*tdp; + sigma1[0][5] = nx*tdp*tdp; + sigma1[1][5] = npx*tdp*tdp; + sigma1[1][0] = sigma1[0][1]; + sigma1[5][0] = sigma1[0][5]; + sigma1[5][1] = sigma1[1][5]; + sigma1[2][2] = tey*by+ny*ny*tdp*tdp; + sigma1[2][3] = -tey*ay+ny*npy*tdp*tdp; + sigma1[3][3] = tey*gy+npy*npy*tdp*tdp; + sigma1[2][5] = ny*tdp*tdp; + sigma1[3][5] = npy*tdp*tdp; + sigma1[3][2] = sigma1[2][3]; + sigma1[5][2] = sigma1[2][5]; + sigma1[5][3] = sigma1[3][5]; + + + double begtocnt[6][6]={ + { 0.65954, 0.43311, 0.00321, 0.10786, 0.00000, 1.97230}, + { 0.13047, 1.60192, 0.00034, 0.00512, 0.00000, 1.96723}, + {-0.00287, -0.03677, -0.35277, -4.68056, 0.00000, 0.68525}, + {-0.00089, -0.00430, -0.17722, -5.18616, 0.00000, 0.32300}, + {-0.00104, 0.00232, -0.00001, -0.00224, 1.00000, -0.00450}, + { 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 1.00000} + }; + double cnttoups[6][6]={{0}}; + double cnttodns[6][6]={{0}}; + double identity[6][6]={{0}}; + double begtoups[6][6]={{0}}; + double begtodns[6][6]={{0}}; + for (int i=0;i<6;i++) { + for (int j=0;j<6;j++) { + if (i==j) { + cnttoups[i][j] = 1.0; + cnttodns[i][j] = 1.0; + identity[i][j] = 1.0; + } else { + cnttoups[i][j] = 0.0; + cnttodns[i][j] = 0.0; + identity[i][j] = 0.0; + } + } + } + cnttoups[0][1] = -0.35710; + cnttoups[2][3] = -0.35710; + cnttodns[0][1] = +0.35710; + cnttodns[2][3] = +0.35710; + for (int i=0;i<6;i++) { + for (int j=0;j<6;j++) { + for (int k=0;k<6;k++) { + begtoups[i][k] = begtoups[i][k] + cnttoups[i][j]*begtocnt[j][k]; + begtodns[i][k] = begtodns[i][k] + cnttodns[i][j]*begtocnt[j][k]; + } + } + } + //swim to upstream of target + double cx, cy, sx, sy, rho; + sbn::swimBNB(centroid1,sigma1, + cnttoups, begtoups, + cx, cy, sx, sy, rho); + double scalex=tgtsx/sx; + double scaley=tgtsy/sy; + double fom_a=sbn::func_intbivar(cx, cy, sx*scalex, sy*scaley, rho); + //swim to center of target + sbn::swimBNB(centroid1,sigma1, + identity, begtocnt, + cx, cy, sx, sy, rho); + double fom_b=sbn::func_intbivar(cx, cy, sx*scalex, sy*scaley, rho); + //swim to downstream of target + sbn::swimBNB(centroid1,sigma1, + cnttodns, begtodns, + cx, cy, sx, sy, rho); + double fom_c=sbn::func_intbivar(cx, cy, sx*scalex, sy*scaley, rho); + // add a guard for double precision + + if(fom_a <= -10000. || fom_b <= -10000. || fom_c <= -10000) return -10000.; + double fom2=fom_a*0.6347 + + fom_b*0.2812 + + fom_c*0.0841; + return fom2; + } + + void swimBNB(const double centroid1[6], const double sigma1[6][6], + const double xferc[6][6], const double xfers[6][6], + double &cx, double& cy, double &sx, double &sy, double &rho) + { + //centroid + double centroid2[6]={0}; + for (int i=0;i<6;i++) { + for (int j=0;j<6;j++) { + centroid2[i] = centroid2[i] + xferc[i][j]*centroid1[j]; + } + } + cx = centroid2[0]; + cy = centroid2[2]; + //sigma + double sigma2[6][6]={{0}}; + for (int i = 0; i<6;i++) { + for (int j = 0;j<6;j++) { + for (int k = 0;k<6;k++) { + for (int m = 0;m<6;m++) { + sigma2[i][m] = sigma2[i][m] + xfers[i][j]*sigma1[j][k]*xfers[m][k]; + } + } + } + } + //get beam sigma + sx = sqrt(sigma2[0][0])*1000.0; + sy = sqrt(sigma2[2][2])*1000.0; + rho = sigma2[0][2]/sqrt(sigma2[0][0]*sigma2[2][2]); + } + + + + double func_intbivar(const double cx, const double cy, const double sx, const double sy, const double rho ) + { + //integrate beam overlap with target cylinder + double x0 = cx; + double y0 = cy; + double dbin = 0.1; + double dx = dbin; + double dy = dbin; + double r = 4.75; + double rr = r*r; + double rho2 = rho*rho; + double xmin = -r; + double ymin = -r; + int imax = round((2.0*r)/dx); + int jmax = round((2.0*r)/dy); + double sum = 0.0; + double x = xmin; + for (int i=0;i<=imax;i++) { + double y = ymin; + for (int j=0;j<=jmax;j++) { + if ( (x*x+y*y)= 1.) return -10000.; + return log10(1-sum); + } + +} diff --git a/sbncode/BeamSpillInfoRetriever/getFOM.h b/sbncode/BeamSpillInfoRetriever/getFOM.h new file mode 100644 index 000000000..2015ea937 --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/getFOM.h @@ -0,0 +1,48 @@ +#ifndef SBNCODE_BEAMSPILLRETRIEVER_GETFOM_H +#define SBNCODE_BEAMSPILLRETRIEVER_GETFOM_H + +/** + * @file sbncode/BeamSpillInfoRetriever/getFOM.h + * @brief Beam quality figures of merit. + * @author Max Dubnowski (maxdub@upenn.sas.edu) + */ + +#include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" + + + +namespace sbn +{ + /** + * @brief Returns a Figure of Merit on BNB beam quality. + * + * The figure of merit is described in [SBN DocDB 41901](https://sbn-docdb.fnal.gov/cgi-bin/sso/ShowDocument?docid=41901). + * Inputs the BNBSpillInfo and returns the BNB Quality Metric called FOM, derived from MicroBooNE's FOM + */ + float getBNBqualityFOM(BNBSpillInfo& spill); + + /** + * @brief Inside the getFOM script, takes the positions and angles of the beam and calculates the BNB FOM + */ + double calcFOM(double horpos,double horang,double verpos,double verang,double tor,double tgtsx,double tgtsy); + + /** + * @brief Takes in the centroid and sigma of the beam, along with transfer matrices, and will determine the beam's + * 2D gaussian position depending where on the target is being measured. The code "swims" up the target to calculate these + */ + void swimBNB(const double centroid1[6], const double sigma1[6][6], + const double xferc[6][6], const double xfers[6][6], + double &cx, double& cy, double &sx, double &sy, double &rho); + + /** + * @brief Integrates the 2D modelled gaussian beam overlapping with the target, and returns the fraction outside the beam + */ + double func_intbivar(const double cx, const double cy, const double sx, const double sy, const double rho ); + + /** + * @brief Inputs the MWR Data and determines the centroid, sigma, and chi2 value of a gaussian fit of the beam + */ + void processBNBprofile(const double* mwdata, double &x, double& sx, double& chi2); + +} +#endif diff --git a/sbncode/BeamSpillInfoRetriever/job/bnbextcountinfo.fcl b/sbncode/BeamSpillInfoRetriever/job/bnbextcountinfo.fcl deleted file mode 100644 index e8191e2dc..000000000 --- a/sbncode/BeamSpillInfoRetriever/job/bnbextcountinfo.fcl +++ /dev/null @@ -1,10 +0,0 @@ - -BEGIN_PROLOG - -bnbextcountinfo: { - - module_type: "BNBEXTRetriever" - raw_data_label: "daq" - -} -END_PROLOG diff --git a/sbncode/BeamSpillInfoRetriever/job/icarusbnbextcountinfo.fcl b/sbncode/BeamSpillInfoRetriever/job/icarusbnbextcountinfo.fcl new file mode 100644 index 000000000..cabe02824 --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/job/icarusbnbextcountinfo.fcl @@ -0,0 +1,10 @@ + +BEGIN_PROLOG + +icarusbnbextcountinfo: { + + module_type: "ICARUSBNBEXTRetriever" + raw_data_label: "daq" + +} +END_PROLOG diff --git a/sbncode/BeamSpillInfoRetriever/job/bnbspillinfo.fcl b/sbncode/BeamSpillInfoRetriever/job/icarusbnbspillinfo.fcl similarity index 57% rename from sbncode/BeamSpillInfoRetriever/job/bnbspillinfo.fcl rename to sbncode/BeamSpillInfoRetriever/job/icarusbnbspillinfo.fcl index 3aece8658..03c63ab3e 100644 --- a/sbncode/BeamSpillInfoRetriever/job/bnbspillinfo.fcl +++ b/sbncode/BeamSpillInfoRetriever/job/icarusbnbspillinfo.fcl @@ -1,13 +1,16 @@ BEGIN_PROLOG -bnbspillinfo: { +icarusbnbspillinfo: { - module_type: "BNBRetriever" + module_type: "ICARUSBNBRetriever" TimePadding: 0.0333 #unit seconds, Booster Rep Rate is 15 Hz, so the closest spill could be 66ms away + BESOffset: 0.035746 #unit seconds, we expect the BES to be about 35 ms from $1D. Need to adjust search range accordingly. URL: "" #keep this blank and we're good - Bundle: "BoosterNeutrinoBeam_read" + Bundle: "BoosterNeutrinoBeam_read_expanded" MultiWireBundle: "BNBMultiWire" + VP873Bundle: "BNB_monitor" + OffsetBundle: "BNB_BPM_settings" TimeWindow: "700" #seconds MWR_TimeWindow: "3601" #seconds raw_data_label: "daq" diff --git a/sbncode/BeamSpillInfoRetriever/job/run_bnbextinfo_sbn.fcl b/sbncode/BeamSpillInfoRetriever/job/run_bnbextinfo_sbn.fcl deleted file mode 100644 index fbee5871f..000000000 --- a/sbncode/BeamSpillInfoRetriever/job/run_bnbextinfo_sbn.fcl +++ /dev/null @@ -1,32 +0,0 @@ -#include "bnbextcountinfo.fcl" - -process_name: BNBEXTInfoGen - -services:{ - IFBeam:{} -} - - -source: { - -} - -physics: { - producers: { - bnbextinfo: @local::bnbextcountinfo - } - - simulate: [bnbextinfo ] - stream1: [ out1 ] - -} - -outputs: { - out1: { - module_type: RootOutput - fileName: "%ifb_%tc_bnbextinfo.root" - dataTier: "raw" - compressionLevel: 1 - } -} - diff --git a/sbncode/BeamSpillInfoRetriever/job/run_icarusbnbextinfo_sbn.fcl b/sbncode/BeamSpillInfoRetriever/job/run_icarusbnbextinfo_sbn.fcl new file mode 100644 index 000000000..c73bd8fc6 --- /dev/null +++ b/sbncode/BeamSpillInfoRetriever/job/run_icarusbnbextinfo_sbn.fcl @@ -0,0 +1,32 @@ +#include "icarusbnbextcountinfo.fcl" + +process_name: ICARUSBNBEXTInfoGen + +services:{ + IFBeam:{} +} + + +source: { + +} + +physics: { + producers: { + icarusbnbextinfo: @local::icarusbnbextcountinfo + } + + simulate: [icarusbnbextinfo ] + stream1: [ out1 ] + +} + +outputs: { + out1: { + module_type: RootOutput + fileName: "%ifb_%tc_icarusbnbextinfo.root" + dataTier: "raw" + compressionLevel: 1 + } +} + diff --git a/sbncode/BeamSpillInfoRetriever/job/run_bnbinfo_sbn.fcl b/sbncode/BeamSpillInfoRetriever/job/run_icarusbnbinfo_sbn.fcl similarity index 85% rename from sbncode/BeamSpillInfoRetriever/job/run_bnbinfo_sbn.fcl rename to sbncode/BeamSpillInfoRetriever/job/run_icarusbnbinfo_sbn.fcl index 34f8f689d..495b3543b 100644 --- a/sbncode/BeamSpillInfoRetriever/job/run_bnbinfo_sbn.fcl +++ b/sbncode/BeamSpillInfoRetriever/job/run_icarusbnbinfo_sbn.fcl @@ -1,4 +1,4 @@ -#include "bnbspillinfo.fcl" +#include "icarusbnbspillinfo.fcl" process_name: BNBInfoGen @@ -28,10 +28,10 @@ source: { physics: { producers: { - bnbinfo: @local::bnbspillinfo + bnbinfo: @local::icarusbnbspillinfo } - simulate: [bnbinfo ] + simulate: [ bnbinfo ] stream1: [ out1 ] } diff --git a/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbextinfo_sbn.fcl b/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbextinfo_sbn.fcl index ca85d36af..af2f8d1ac 100644 --- a/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbextinfo_sbn.fcl +++ b/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbextinfo_sbn.fcl @@ -1,6 +1,6 @@ #include "sbndbnbdefaults.fcl" -process_name: SBNDBNBEXTInfoGen +process_name: BNBEXTInfoGen services:{ @@ -27,21 +27,21 @@ source: { physics: { producers: { - sbndbnbextinfo: @local::sbndbnbspillinfo + bnbextinfo: @local::sbndbnbspillinfo } - simulate: [sbndbnbextinfo ] + simulate: [bnbextinfo ] stream1: [ out1 ] } -physics.producers.sbndbnbextinfo.module_type: "SBNDBNBEXTRetriever" +physics.producers.bnbextinfo.module_type: "SBNDBNBEXTRetriever" outputs: { out1: { module_type: RootOutput - fileName: "%ifb_%tc_sbndbnbextinfo.root" + fileName: "%ifb_%tc_bnbextinfo.root" dataTier: "raw" compressionLevel: 1 } } -physics.producers.sbndbnbextinfo.fileNames: @local::outputs.out1.fileName +physics.producers.bnbextinfo.fileNames: @local::outputs.out1.fileName diff --git a/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbinfo_sbn.fcl b/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbinfo_sbn.fcl index 66756c41b..602bfe3f3 100644 --- a/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbinfo_sbn.fcl +++ b/sbncode/BeamSpillInfoRetriever/job/run_sbndbnbinfo_sbn.fcl @@ -1,6 +1,6 @@ #include "sbndbnbdefaults.fcl" -process_name: SBNDBNBInfoGen +process_name: BNBInfoGen services:{ @@ -28,21 +28,21 @@ source: { physics: { producers: { - sbndbnbinfo: @local::sbndbnbspillinfo + bnbinfo: @local::sbndbnbspillinfo } - simulate: [sbndbnbinfo ] + simulate: [bnbinfo ] stream1: [ out1 ] } -physics.producers.sbndbnbinfo.module_type: "SBNDBNBRetriever" +physics.producers.bnbinfo.module_type: "SBNDBNBRetriever" outputs: { out1: { module_type: RootOutput - fileName: "%ifb_%tc_sbndbnbinfo.root" + fileName: "%ifb_%tc_bnbinfo.root" dataTier: "raw" compressionLevel: 1 } } -physics.producers.sbndbnbinfo.fileNames: @local::outputs.out1.fileName +physics.producers.bnbinfo.fileNames: @local::outputs.out1.fileName diff --git a/sbncode/BeamSpillInfoRetriever/job/sbndbnbdefaults.fcl b/sbncode/BeamSpillInfoRetriever/job/sbndbnbdefaults.fcl index 26327a100..47f37d9ba 100644 --- a/sbncode/BeamSpillInfoRetriever/job/sbndbnbdefaults.fcl +++ b/sbncode/BeamSpillInfoRetriever/job/sbndbnbdefaults.fcl @@ -6,10 +6,13 @@ sbndbnbspillinfo: { TimePadding: 0.0333 #unit seconds, Booster Rep Rate is 15 Hz, so the closest spill could be 66ms away was 0.0333 BESOffset: 0.035 #unit seconds, we expect the BES to be about 35 ms from $1D. Need to adjust search range accordingly. URL: "" #Default URL having issues Nov 21 2024, used this instead https://dbdata3vm.fnal.gov:9443/ifbeam - Bundle: "BoosterNeutrinoBeam_read" + Bundle: "BoosterNeutrinoBeam_read_expanded" MultiWireBundle: "BNBMultiWire" + VP873Bundle: "BNB_monitor" + OffsetBundle: "BNB_BPM_settings" + TimeWindow: "700" #seconds - MWR_TimeWindow: "3601" #seconds + MWR_TimeWindow: "700" #seconds DeviceUsedForTiming: "E:TOR860" } diff --git a/sbncode/BeamSpillInfoRetriever/job/sbndbnbspillinfo.fcl b/sbncode/BeamSpillInfoRetriever/job/sbndbnbspillinfo.fcl deleted file mode 100644 index 26327a100..000000000 --- a/sbncode/BeamSpillInfoRetriever/job/sbndbnbspillinfo.fcl +++ /dev/null @@ -1,17 +0,0 @@ -BEGIN_PROLOG - -sbndbnbspillinfo: { - module_type: "" - DebugLevel: 0 - TimePadding: 0.0333 #unit seconds, Booster Rep Rate is 15 Hz, so the closest spill could be 66ms away was 0.0333 - BESOffset: 0.035 #unit seconds, we expect the BES to be about 35 ms from $1D. Need to adjust search range accordingly. - URL: "" #Default URL having issues Nov 21 2024, used this instead https://dbdata3vm.fnal.gov:9443/ifbeam - Bundle: "BoosterNeutrinoBeam_read" - MultiWireBundle: "BNBMultiWire" - TimeWindow: "700" #seconds - MWR_TimeWindow: "3601" #seconds - DeviceUsedForTiming: "E:TOR860" -} - -END_PROLOG - diff --git a/sbncode/CAFMaker/CAFMakerParams.h b/sbncode/CAFMaker/CAFMakerParams.h index d05eb8b9b..9a5d8fb40 100644 --- a/sbncode/CAFMaker/CAFMakerParams.h +++ b/sbncode/CAFMaker/CAFMakerParams.h @@ -16,6 +16,7 @@ namespace caf template using Atom = fhicl::Atom; template using Sequence = fhicl::Sequence; template using Table = fhicl::Table; + template using OptionalTable = fhicl::OptionalTable; using Comment = fhicl::Comment; using Name = fhicl::Name; using string = std::string; @@ -44,6 +45,10 @@ namespace caf Atom SaveGENIEEventRecord { Name("SaveGENIEEventRecord"), Comment("Whether to produce GENIE event record to the output file"), false }; + + Atom OverrideRealData { Name("OverrideRealData"), + Comment("when true, some algorithms (e.g. PoT count) treat events as MC rather than real data -- e.g. set it if the event is an overlay"), false + }; Atom PrescaleFactor { Name("PrescaleFactor"), Comment("Factor by which to prescale unblind events"), 10 @@ -250,6 +255,12 @@ namespace caf "pandoraPid" }; + Atom TrackLikePidLabel { + Name("TrackLikePidLabel"), + Comment("Base label of track likelihood particle-id producer."), + "pandoraLikePid" + }; + Atom TrackScatterClosestApproachLabel { Name("TrackScatterClosestApproachLabel"), Comment("Base label of track track scatter closestapproach producer."), @@ -274,6 +285,12 @@ namespace caf "pandoraTrackCRTHit" }; + Atom CRTHitMatchInfoLabel { + Name("CRTHitMatchInfoLabel"), + Comment("Base label of additional information on track to CRT hit matching producer."), + "CRTT0Tagging" + }; + Atom CRTTrackMatchLabel { Name("CRTTrackMatchLabel"), Comment("Base label of track to CRT track matching producer."), @@ -310,6 +327,12 @@ namespace caf "crthit" // icarus }; + Atom CRTSimChanLabel { + Name("CRTSimChanLabel"), + Comment("Label of AuxDetSimChannels."), + "genericcrt" // icarus + }; + Atom CRTTrackLabel { Name("CRTTrackLabel"), Comment("Label of sbn CRT tracks."), @@ -328,6 +351,30 @@ namespace caf "crttracks" // sbnd }; + Atom SBNDCRTVetoLabel { + Name("SBNDCRTVetoLabel"), + Comment("Label of sbnd CRT Veto."), + "crtveto" // sbnd + }; + + Atom SBNDFrameShiftInfoLabel { + Name("SBNDFrameShiftInfoLabel"), + Comment("Label of sbnd frame shift."), + "" // sbnd + }; + + Atom SBNDTimingInfoLabel { + Name("SBNDTimingInfoLabel"), + Comment("Label of sbnd timing shift."), + "" // sbnd + }; + + Atom SBNDSoftwareTriggerLabel{ + Name("SBNDSoftwareTriggerLabel"), + Comment("Label for software trigger producer"), + "" // sbnd + }; + Atom CRTPMTLabel { Name("CRTPMTLabel"), Comment("Label for the CRTPMT Matched variables from the crtpmt data product"), @@ -340,12 +387,42 @@ namespace caf "" //Empty by default, configured in icaruscode cafmaker_defs }; + Atom CorrectedOpFlashLabel { + Name("CorrectedOpFlashLabel"), + Comment("Label of CorrectedOpFlash containing tpc-corrected flash time."), + "" + }; + + Atom NuGraphSliceHitLabel { + Name("NuGraphSliceHitLabel"), + Comment("Label of NuGraph slice hit map."), + "" //Empty by default, please set to e.g. art::InputTag("nuslhits") + }; + + Atom NuGraphFilterLabel { + Name("NuGraphFilterLabel"), + Comment("Label of NuGraph filter."), + "" //Empty by default, please set to e.g. art::InputTag("NuGraph","filter") + }; + + Atom NuGraphSemanticLabel { + Name("NuGraphSemanticLabel"), + Comment("Label of NuGraph semantic."), + "" //Empty by default, please set to e.g. art::InputTag("NuGraph","semantic") + }; + Atom OpFlashLabel { Name("OpFlashLabel"), Comment("Label of PMT flash."), "OpFlash" }; + Atom PMTBeamSignalLabel { + Name("PMTBeamSignalLabel"), + Comment("Label for special PMT beam timing signals used to build the beam bunch structure"), + "beamTiming:RWM" + }; + Atom CRTSimT0Offset { Name("CRTSimT0Offset"), Comment("start of beam gate/simulation time in the simulated CRT clock"), @@ -364,6 +441,30 @@ namespace caf "emuTriggerUnshifted" }; + Atom MonPulsesTriggerLabel { + Name("MonPulsesTriggerLabel"), + Comment("Label of trigger emulation product MonPulses (number of PMT pairs above threshold for all channels) for all flashes."), + art::InputTag("opdaq", "MonPulses", "DetSim") + }; + + Atom MonPulseSizesTriggerLabel { + Name("MonPulseSizesTriggerLabel"), + Comment("Label of trigger emulation product MonPulses Sizes, which gives the length of each trigger response in MonPulses."), + art::InputTag("opdaq", "MonPulseSizes", "DetSim") + }; + + Atom PairsTriggerLabel { + Name("PairsTriggerLabel"), + Comment("Label of number of PMT pairs over threshold."), + art::InputTag("opdaq", "pairsOverThreshold", "DetSim") + }; + + Atom EmulatedTriggerLabel { + Name("EmulatedTriggerLabel"), + Comment("Label of bool of passing the trigger."), + art::InputTag("opdaq", "triggerEmulation", "DetSim") + }; + Atom FlashTrigLabel { Name("FlashTrigLabel"), Comment("Label of bool of passing flash trigger."), @@ -382,6 +483,12 @@ namespace caf "simdrift" }; + Atom SimEnergyDepositLabel { + Name("SimEnergyDepositLabel"), + Comment("Label of input sim::SimEnergyDeposit objects."), + art::InputTag("ionandscint", "priorSCE","G4") + }; + Atom FillTrueParticles { Name("FillTrueParticles"), Comment("Whether to fill the rec.true_particles branch. The information on true particles" @@ -424,6 +531,92 @@ namespace caf 25. }; + struct PFOCharLabels_t { + Atom EndFractionName { + Name("EndFractionName"), + Comment("Provide the tool name for the EndFraction BDT variable."), + "LArThreeDChargeFeatureTool_EndFraction" + }; + + Atom FractionalSpreadName { + Name("FractionalSpreadName"), + Comment("Provide the tool name for the FractionalSpread BDT variable."), + "LArThreeDChargeFeatureTool_FractionalSpread" + }; + + Atom DiffStraightLineMeanName { + Name("DiffStraightLineMeanName"), + Comment("Provide the tool name for the DiffStraightLineMean BDT variable."), + "LArThreeDLinearFitFeatureTool_DiffStraightLineMean" + }; + + Atom LengthName { + Name("LengthName"), + Comment("Provide the tool name for the Length BDT variable."), + "LArThreeDLinearFitFeatureTool_Length" + }; + + Atom MaxFitGapLengthName { + Name("MaxFitGapLengthName"), + Comment("Provide the tool name for the MaxFitGapLength BDT variable."), + "LArThreeDLinearFitFeatureTool_MaxFitGapLength" + }; + + Atom SlidingLinearFitRMSName { + Name("SlidingLinearFitRMSName"), + Comment("Provide the tool name for the SlidingLinearFitRMS BDT variable."), + "LArThreeDLinearFitFeatureTool_SlidingLinearFitRMS" + }; + + Atom AngleDiffName { + Name("AngleDiffName"), + Comment("Provide the tool name for the AngleDiff BDT variable."), + "LArThreeDOpeningAngleFeatureTool_AngleDiff" + }; + + Atom SecondaryPCARatioName { + Name("SecondaryPCARatioName"), + Comment("Provide the tool name for the SecondaryPCARatio BDT variable."), + "LArThreeDPCAFeatureTool_SecondaryPCARatio" + }; + + Atom TertiaryPCARatioName { + Name("TertiaryPCARatioName"), + Comment("Provide the tool name for the TertiaryPCARatio BDT variable."), + "LArThreeDPCAFeatureTool_TertiaryPCARatio" + }; + + Atom VertexDistanceName { + Name("VertexDistanceName"), + Comment("Provide the tool name for the VertexDistance BDT variable."), + "LArThreeDVertexDistanceFeatureTool_VertexDistance" + }; + + Atom HaloTotalRatioName { + Name("HaloTotalRatioName"), + Comment("Provide the tool name for the HaloTotalRatio BDT variable."), + "LArConeChargeFeatureTool_HaloTotalRatio" + }; + + Atom ConcentrationName { + Name("ConcentrationName"), + Comment("Provide the tool name for the Concentration BDT variable."), + "LArConeChargeFeatureTool_Concentration" + + }; + + Atom ConicalnessName { + Name("ConicalnessName"), + Comment("Provide the tool name for the Conicalness BDT variable."), + "LArConeChargeFeatureTool_Conicalness" + }; + }; + + OptionalTable PFOCharLabels { + Name("PFOCharLabels"), + Comment("Provide tool names for the Pandora track/shower discrimination BDT variables.") + }; + Atom ReferencePMTFromTriggerToBeam { Name("ReferencePMTFromTriggerToBeam"), Comment("Whether to switch the reference time of PMT reco from 'trigger' to 'beam spill' time."), @@ -447,7 +640,18 @@ namespace caf Comment("Label of CVN scores."), "cvn" }; + + Atom LightCaloLabel { + Name("LightCaloLabel"), + Comment("Label of light calorimetry producer"), + "lightcalo" + }; + Atom fBlipTag { + Name("BlipTag"), + Comment("Provide a string to label the blip input"), + "blipreco" + }; }; } diff --git a/sbncode/CAFMaker/CAFMaker_module.cc b/sbncode/CAFMaker/CAFMaker_module.cc index 2728b395f..4cc2fcffe 100644 --- a/sbncode/CAFMaker/CAFMaker_module.cc +++ b/sbncode/CAFMaker/CAFMaker_module.cc @@ -20,6 +20,7 @@ #include "sbncode/CAFMaker/FillExposure.h" #include "sbncode/CAFMaker/FillTrigger.h" #include "sbncode/CAFMaker/Utils.h" +#include "sbncode/CAFMaker/FillBlip.h" // C/C++ includes #include @@ -95,12 +96,16 @@ #include "lardataobj/RecoBase/Vertex.h" #include "lardataobj/RecoBase/Shower.h" #include "lardataobj/RecoBase/MCSFitResult.h" +#include "lardataobj/RecoBase/Cluster.h" +#include "lardataobj/AnalysisBase/MVAOutput.h" +#include "lardataobj/Simulation/SimEnergyDeposit.h" #include "nusimdata/SimulationBase/MCFlux.h" #include "nusimdata/SimulationBase/MCTruth.h" #include "nusimdata/SimulationBase/MCNeutrino.h" #include "nusimdata/SimulationBase/GTruth.h" +#include "sbnobj/Common/CRT/CRTHitT0TaggingInfo.hh" #include "sbnobj/Common/EventGen/MeVPrtl/MeVPrtlTruth.h" #include "sbnobj/Common/Reco/RangeP.h" #include "sbnobj/Common/SBNEventWeight/EventWeightMap.h" @@ -112,9 +117,16 @@ #include "sbnobj/Common/POTAccounting/BNBSpillInfo.h" #include "sbnobj/Common/POTAccounting/EXTCountInfo.h" #include "sbnobj/Common/POTAccounting/NuMISpillInfo.h" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" +#include "artdaq-core/Data/ContainerFragment.hh" #include "sbnobj/Common/Trigger/ExtraTriggerInfo.h" #include "sbnobj/Common/Reco/CRUMBSResult.h" #include "sbnobj/Common/Reco/OpT0FinderResult.h" +#include "sbnobj/SBND/CRT/CRTVeto.hh" +#include "sbnobj/Common/Reco/CorrectedOpFlashTiming.h" +#include "sbnobj/Common/Reco/LightCalo.h" +#include "sbnobj/SBND/Timing/TimingInfo.hh" +#include "sbnobj/SBND/Timing/FrameShiftInfo.hh" // GENIE #include "Framework/EventGen/EventRecord.h" @@ -134,6 +146,7 @@ #include "sbnanaobj/StandardRecord/Flat/FlatRecord.h" #include "lardataobj/RawData/ExternalTrigger.h" #include "lardataobj/RawData/TriggerData.h" +#include "lardataobj/Simulation/AuxDetSimChannel.h" // // CAFMaker #include "sbncode/CAFMaker/AssociationUtil.h" @@ -200,6 +213,9 @@ class CAFMaker : public art::EDProducer { std::string fSourceFile; std::uint32_t fSourceFileHash; + bool fNewInputFile; + + bool fOverrideRealData; bool fFirstInSubRun; unsigned int fIndexInFile = SRHeader::NoSourceIndex; bool fFirstBlindInSubRun; @@ -215,6 +231,7 @@ class CAFMaker : public art::EDProducer { double fTotalEvents; double fBlindEvents; double fPrescaleEvents; + double fTotalGenEvents; std::vector fBNBInfo; ///< Store detailed BNB info to save into the first StandardRecord of the output file std::vector fNuMIInfo; ///< Store detailed NuMI info to save into the first StandardRecord of the output file std::map fBNBInfoEventMap; ///< Store detailed BNB info to save for the particular spills of events @@ -255,6 +272,8 @@ class CAFMaker : public art::EDProducer { double fGenieEvtRec_brEvtXSec = 0.0; ////< Cross section for selected event (1e-38 cm2) double fGenieEvtRec_brEvtDXSec = 0.0; ////< Cross section for selected event kinematics (1e-38 cm2 / {K^n}) unsigned int fGenieEvtRec_brEvtKPS = 0; ////< Kinematic phase space variables. See $GENIE/src/Framework/Conventions/KinePhaseSpace.h -> KinePhaseSpace_t + int fGenieEvtRec_brSctType = 0; ///< See [`genie::EScatteringType`](https://hep.ph.liv.ac.uk/~costasa/genie_doxygen/master/html/namespacegenie.html#ab97d2b4d1f37af8d967dadd15be88d0b) + int fGenieEvtRec_brIntType = 0; ///< See [`genie::EInteractionType`](https://hep.ph.liv.ac.uk/~costasa/genie_doxygen/master/html/namespacegenie.html#a554f81bb9954c9e46bbabadfcd403111) double fGenieEvtRec_brEvtWght = 0.0; ////< Weight for that event double fGenieEvtRec_brEvtProb = 0.0; ////< Probability for that event (given cross section, path lengths, etc) double fGenieEvtRec_brEvtVtx[4] = {0.0}; ////< Event vertex position in detector coord syst (SI) @@ -280,6 +299,11 @@ class CAFMaker : public art::EDProducer { std::vector> fTPCVolumes; std::vector fActiveVolumes; + // CRT geometry info + // + // ICARUS + std::map, int> fFEBChannel2AuxDetID; + // random number generator for fake reco CLHEP::HepRandomEngine& fFakeRecoRandomEngine; @@ -308,10 +332,14 @@ class CAFMaker : public art::EDProducer { double GetBlindPOTScale() const; void InitVolumes(); ///< Initialize volumes from Gemotry service + void InitCRTMapping(); ///< Initialize CRT mapping void FixPMTReferenceTimes(StandardRecord &rec, double PMT_reference_time); void FixCRTReferenceTimes(StandardRecord &rec, double CRTT0_reference_time, double CRTT1_reference_time); + void SBNDShiftCRTReference(StandardRecord &rec, double SBNDFrame) const; + void SBNDShiftPMTReference(StandardRecord &rec, double SBNDFrame) const; + /// Equivalent of FindManyP except a return that is !isValid() prints a /// messsage and aborts if StrictMode is true. template @@ -329,6 +357,10 @@ class CAFMaker : public art::EDProducer { art::FindOneP FindOnePStrict(const U& from, const art::Event& evt, const art::InputTag& label) const; + template + art::FindOneP FindOnePStrictSingle(const U& from, const art::Event& evt, + const art::InputTag& label) const; + template art::FindOneP FindOnePDStrict(const U& from, const art::Event& evt, @@ -401,6 +433,7 @@ class CAFMaker : public art::EDProducer { // Note: we will define isRealData on a per event basis in produce function [using event.isRealData()], at least for now. fCafFilename = fParams.CAFFilename(); + fOverrideRealData = fParams.OverrideRealData(); fFlatCafFilename = fParams.FlatCAFFilename(); // Normally CAFMaker is run wit no output ART stream, so these go @@ -412,6 +445,9 @@ class CAFMaker : public art::EDProducer { // setup volume definitions InitVolumes(); + // setup CRT mapping + InitCRTMapping(); + fSaveGENIEEventRecord = fParams.SaveGENIEEventRecord(); } @@ -494,6 +530,44 @@ void CAFMaker::BlindEnergyParameters(StandardRecord* brec) { } } +void CAFMaker::SBNDShiftCRTReference(StandardRecord &rec, double SBNDFrame) const { + + //CRT Space Point + for (SRCRTSpacePoint &sp: rec.crt_spacepoints){ + sp.time += SBNDFrame; //ns + } + + //CRT Track + for (SRSBNDCRTTrack &trk: rec.sbnd_crt_tracks){ + trk.time += SBNDFrame; //ns + } + + //TODO: CRT Space Point and Track Match + for (SRSlice &slc: rec.slc){ + for (SRPFP &pfp: slc.reco.pfp){ + if(!std::isnan(pfp.trk.crtspacepoint.score)) pfp.trk.crtspacepoint.spacepoint.time += SBNDFrame; + + if(!std::isnan(pfp.trk.crtsbndtrack.score)) pfp.trk.crtsbndtrack.track.time += SBNDFrame; + } + } +} + +void CAFMaker::SBNDShiftPMTReference(StandardRecord &rec, double SBNDFrame) const { + + double SBNDFrame_us = SBNDFrame / 1000.0; //convert ns to us + + //Op Flash + for (SROpFlash &opf: rec.opflashes) { + opf.time += SBNDFrame_us; + opf.firsttime += SBNDFrame_us; + } + + //OpT0 match to slice + for (SRSlice &s: rec.slc) { + s.opt0.time += SBNDFrame_us; + } +} + void CAFMaker::FixPMTReferenceTimes(StandardRecord &rec, double PMT_reference_time) { // Fix the flashes for (SROpFlash &f: rec.opflashes) { @@ -547,6 +621,36 @@ void CAFMaker::FixCRTReferenceTimes(StandardRecord &rec, double CRTT0_reference_ } +void CAFMaker::InitCRTMapping() { + + // ICARUS + cet::search_path searchPath("FW_SEARCH_PATH"); + std::string fullFileName; + searchPath.find_file("feb_map.txt",fullFileName); + std::ifstream fin; + fin.open(fullFileName, std::ios::in); + + if (fin.good()) { + std::string line; + + while(std::getline(fin,line)) { + std::vector row; + std::string word; + + std::stringstream s(line); + while (std::getline(s, word, ',')) { + row.push_back(word); + } + int auxDetID = std::stoi(row[0]); // auxDetID + int mac5 = std::stoi(row[1]); // FEB ID + int chan = std::stoi(row[2]); // FEB channel + fFEBChannel2AuxDetID[std::make_pair(mac5, chan)] = auxDetID; + } + + fin.close(); + } +} + void CAFMaker::InitVolumes() { const geo::GeometryCore *geometry = lar::providerFrom(); @@ -693,6 +797,7 @@ void CAFMaker::respondToOpenInputFile(const art::FileBlock& fb) { // so should be less than or equal to 32-bit fSourceFileHash = static_cast(fSourceFileHashFull); + fNewInputFile = true; } //...................................................................... @@ -768,6 +873,18 @@ void CAFMaker::beginRun(art::Run& run) { fDet = override; } + if (std::exchange(fNewInputFile, false)){ + for (const art::ProcessConfiguration &process: run.processHistory()) { + std::optional gen_config = run.getProcessParameterSet(process.processName()); + if (gen_config && gen_config->has_key("source") && gen_config->has_key("source.maxEvents") && gen_config->has_key("source.module_type") ) { + int max_events = gen_config->get("source.maxEvents"); + std::string module_type = gen_config->get("source.module_type"); + if (module_type == "EmptyEvent") { + fTotalGenEvents += max_events; + } + } + } + } if(fParams.SystWeightLabels().empty()) return; // no need for globalTree @@ -847,9 +964,18 @@ void CAFMaker::beginSubRun(art::SubRun& sr) { std::cout << std::endl; abort(); } - + if ( fOverrideRealData ) { + // Expects a generator POT summary then... + if(auto pot_handle = sr.getHandle(fParams.GenLabel())){ + fSubRunPOT = pot_handle->totgoodpot; + fTotalPOT += fSubRunPOT; + }else{ + std::cout << "Did not find MC POT info under " << fParams.GenLabel() << std::endl; + if(fParams.StrictMode()) abort(); + } + }else{ if(bnb_spill){ - FillExposure(*bnb_spill, fBNBInfo, fSubRunPOT); + FillExposure(*bnb_spill, fBNBInfo, fSubRunPOT); fTotalPOT += fSubRunPOT; // Find the spill for each event and fill the event map: @@ -908,10 +1034,9 @@ void CAFMaker::beginSubRun(art::SubRun& sr) { << std::endl; if(fParams.StrictMode()) abort(); } - // Otherwise, if one label is blank, maybe no POT was the expected result } - + } std::cout << "POT: " << fSubRunPOT << std::endl; fFirstInSubRun = true; @@ -1038,7 +1163,6 @@ void CAFMaker::InitializeOutfiles() } - if(fParams.CreateFlatCAF()){ mf::LogInfo("CAFMaker") << "Output flat filename is " << fFlatCafFilename; @@ -1085,6 +1209,8 @@ void CAFMaker::InitializeOutfiles() fFlatGenieTree->Branch("GenieEvtRec.EvtXSec", &fGenieEvtRec_brEvtXSec, "GenieEvtRec.EvtXSec/D" ); fFlatGenieTree->Branch("GenieEvtRec.EvtDXSec", &fGenieEvtRec_brEvtDXSec, "GenieEvtRec.EvtDXSec/D" ); fFlatGenieTree->Branch("GenieEvtRec.EvtKPS", &fGenieEvtRec_brEvtKPS, "GenieEvtRec.EvtKPS/i" ); + fFlatGenieTree->Branch("GenieEvtRec.SctType", &fGenieEvtRec_brSctType, "GenieEvtRec.SctType/I" ); + fFlatGenieTree->Branch("GenieEvtRec.IntType", &fGenieEvtRec_brIntType, "GenieEvtRec.IntType/I" ); fFlatGenieTree->Branch("GenieEvtRec.EvtWght", &fGenieEvtRec_brEvtWght, "GenieEvtRec.EvtWght/D" ); fFlatGenieTree->Branch("GenieEvtRec.EvtProb", &fGenieEvtRec_brEvtProb, "GenieEvtRec.EvtProb/D" ); fFlatGenieTree->Branch("GenieEvtRec.EvtVtx", fGenieEvtRec_brEvtVtx, "GenieEvtRec.EvtVtx[4]/D" ); @@ -1115,6 +1241,7 @@ void CAFMaker::InitializeOutfiles() fTotalEvents = 0; fBlindEvents = 0; fPrescaleEvents = 0; + fTotalGenEvents = 0; fIndexInFile = SRHeader::NoSourceIndex; fFirstInSubRun = false; fFirstBlindInSubRun = false; @@ -1181,6 +1308,14 @@ art::FindOneP CAFMaker::FindOnePStrict(const U& from, return ret; } +//...................................................................... +template +art::FindOneP CAFMaker::FindOnePStrictSingle(const U& from, + const art::Event& evt, + const art::InputTag& tag) const { + return FindOnePStrict(std::vector{ from }, evt, tag); +} + //...................................................................... template art::FindOneP CAFMaker::FindOnePDStrict(const U& from, @@ -1260,11 +1395,12 @@ bool CAFMaker::GetPsetParameter(const fhicl::ParameterSet& pset, //...................................................................... void CAFMaker::produce(art::Event& evt) noexcept { + mf::LogInfo("CAFMaker") << "CAFMaker::produce called for event: " << evt.id(); bool const firstInFile = (fIndexInFile++ == 0); - // is this event real data? - bool isRealData = evt.isRealData(); + // is this event real data? -- BH: if fOverrideRealData, treat it as MC. Otherwise, get the info from the art event. + bool isRealData = !fOverrideRealData && evt.isRealData(); std::unique_ptr> srcol( new std::vector); @@ -1450,6 +1586,8 @@ void CAFMaker::produce(art::Event& evt) noexcept { fGenieEvtRec_brEvtXSec = genie_rec->XSec() * (1e+38/genie::units::cm2); fGenieEvtRec_brEvtDXSec = genie_rec->DiffXSec() * (1e+38/genie::units::cm2); fGenieEvtRec_brEvtKPS = genie_rec->DiffXSecVars(); + fGenieEvtRec_brSctType = genie_rec->Summary()->ProcInfo().ScatteringTypeId(); + fGenieEvtRec_brIntType = genie_rec->Summary()->ProcInfo().InteractionTypeId(); fGenieEvtRec_brEvtWght = genie_rec->Weight(); fGenieEvtRec_brEvtProb = genie_rec->Probability(); fGenieEvtRec_brEvtVtx[0] = genie_rec->Vertex()->X(); @@ -1459,7 +1597,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { int iparticle=0; genie::GHepParticle * p = 0; - while( genie_rec->Particle(iparticle) != 0 ) { + while( (genie_rec->Particle(iparticle) != 0) && (iparticle < 250) ) { p = genie_rec->Particle(iparticle); fGenieEvtRec_brStdHepPdg[iparticle] = p->Pdg(); fGenieEvtRec_brStdHepStatus[iparticle] = (int) p->Status(); @@ -1517,6 +1655,36 @@ void CAFMaker::produce(art::Event& evt) noexcept { } // end for fm } // end for i (mctruths) + // get sim energy deposits if they're there + ::art::Handle> sed_handle; + GetByLabelStrict(evt, fParams.SimEnergyDepositLabel().encode(), sed_handle); + + if (!isRealData && sed_handle.isValid()){ + art::ServiceHandle pi_serv; + + srtruthbranch.dep.reserve(mctruths.size()); + for (size_t n=0; n mctruth = pi_serv->TrackIdToMCTruth_P(trackID); + auto it = std::find(mctruths.begin(), mctruths.end(), mctruth); + if (it == mctruths.end()) continue; + + auto idx = std::distance(mctruths.begin(), it); + srtruthbranch.dep.at(idx).energy += sed.Energy()*1e-3; // GeV + srtruthbranch.dep.at(idx).photons += sed.NumPhotons(); + srtruthbranch.dep.at(idx).electrons += sed.NumElectrons(); + } + } + // get the number of events generated in the gen stage unsigned n_gen_evt = 0; for (const art::ProcessConfiguration &process: evt.processHistory()) { @@ -1543,7 +1711,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { //####################################################### // Fill detector & reco //####################################################### - + //Beam gate and Trigger info art::Handle extratrig_handle; GetByLabelStrict(evt, fParams.TriggerLabel().encode(), extratrig_handle); @@ -1555,16 +1723,60 @@ void CAFMaker::produce(art::Event& evt) noexcept { if (!isRealData) GetByLabelStrict(evt, fParams.UnshiftedTriggerLabel().encode(), unshifted_trig_handle); + //Trigger emulation handles + art::Handle> monpulses_handle; + GetByLabelStrict(evt, fParams.MonPulsesTriggerLabel().encode(), monpulses_handle); + + art::Handle> monpulse_sizes_handle; + GetByLabelStrict(evt, fParams.MonPulseSizesTriggerLabel().encode(), monpulse_sizes_handle); + + art::Handle pairs_handle; + GetByLabelStrict(evt, fParams.PairsTriggerLabel().encode(), pairs_handle); + + art::Handle trigemu_handle; + GetByLabelStrict(evt, fParams.EmulatedTriggerLabel().encode(), trigemu_handle); + + // Check trigger handles const bool isValidTrigger = extratrig_handle.isValid() && trig_handle.isValid() && trig_handle->size() == 1; const bool isValidUnshiftedTrigger = unshifted_trig_handle.isValid() && unshifted_trig_handle->size() == 1; + const bool isValidEmulationTrigger = monpulses_handle.isValid() && monpulse_sizes_handle.isValid() && pairs_handle.isValid() && trigemu_handle.isValid(); const double triggerShift = (isValidUnshiftedTrigger && isValidTrigger)? unshifted_trig_handle->at(0).TriggerTime() - trig_handle->at(0).TriggerTime() : 0.; + // Fill local ExtraTriggerInfo struct + sbn::ExtraTriggerInfo extratrig; + if (extratrig_handle.isValid()) extratrig = *extratrig_handle; + caf::SRTrigger srtrigger; if (isValidTrigger) { FillTrigger(*extratrig_handle, trig_handle->at(0), srtrigger, triggerShift); + if (fDet == kICARUS) + FillTriggerICARUS(*extratrig_handle, srtrigger); + } + // Fill trigger emulation information + if (isValidEmulationTrigger) { + FillTriggerEmulation(monpulses_handle, monpulse_sizes_handle, pairs_handle, trigemu_handle, srtrigger); + } + + + // Fill PTB (Penn Trigger Board) information for SBND real data + if (isRealData && fDet == kSBND) { + art::InputTag PTB_itag("daq", "ContainerPTB"); + art::Handle ptb_frags_handle; + evt.getByLabel(PTB_itag, ptb_frags_handle); + if (ptb_frags_handle.isValid()) { + mf::LogDebug("CAFMaker") << "Found ContainerPTB, extracting PTB triggers..."; + std::vector ptb_triggers = sbn::pot::extractAllPTBInfo(*ptb_frags_handle); + mf::LogDebug("CAFMaker") << "Extracted " << ptb_triggers.size() << " PTB triggers"; + FillPTBTriggersSBND(ptb_triggers, srtrigger); + mf::LogDebug("CAFMaker") << "PTB HLT triggers: " << srtrigger.ptb_hlt_timestamp.size() + << ", LLT triggers: " << srtrigger.ptb_llt_timestamp.size(); + } else { + mf::LogDebug("CAFMaker") << "ContainerPTB not found for event " << evt.id(); + } } + // If not real data, fill in enough of the SRTrigger to make (e.g.) the CRT // time referencing work. TODO: add more stuff to a "MC"-Trigger? // No longer needed with incorporation of trigger emulation in the MC. @@ -1573,6 +1785,8 @@ void CAFMaker::produce(art::Event& evt) noexcept { // } // try to find the result of the Flash trigger if it was run + mf::LogInfo("CAFMaker") << " New Trigger Time = " << srtrigger.global_trigger_det_time << " us\n" + << " New Beam Gate Time = " << srtrigger.beam_gate_det_time << " us"; bool pass_flash_trig = false; art::Handle flashtrig_handle; GetByLabelStrict(evt, fParams.FlashTrigLabel(), flashtrig_handle); @@ -1590,17 +1804,41 @@ void CAFMaker::produce(art::Event& evt) noexcept { std::vector srcrttracks; std::vector srcrtspacepoints; std::vector srsbndcrttracks; + caf::SRSBNDCRTVeto srsbndcrtveto; + caf::SRSBNDFrameShiftInfo srsbndframeshiftinfo; + caf::SRSBNDTimingInfo srsbndtiminginfo; + caf::SRSoftwareTrigger srsbndsofttrig; + + // Mapping of (feb, channel) to truth information (AuxDetSimChannel) -- filled for ICARUS + std::map, sim::AuxDetSimChannel> crtsimchanmap; if(fDet == kICARUS) { art::Handle> crthits_handle; GetByLabelStrict(evt, fParams.CRTHitLabel(), crthits_handle); + + art::Handle> auxdetsimchan_handle; + GetByLabelStrict(evt, fParams.CRTSimChanLabel(), auxdetsimchan_handle); + // fill into event if (crthits_handle.isValid()) { const std::vector &crthits = *crthits_handle; + + std::vector empty; + const std::vector &crtsimchanvec = (auxdetsimchan_handle.isValid()) ? *auxdetsimchan_handle : empty; + // Turn the AuxDetSimChannel's into a map that can be looked up from a CRT Hit + for (const sim::AuxDetSimChannel &crtsimchan: crtsimchanvec) { + for (auto const &map_pair: fFEBChannel2AuxDetID) { + if (map_pair.second == (int)crtsimchan.AuxDetID()) { + crtsimchanmap[map_pair.first] = crtsimchan; + break; + } + } + } + for (unsigned i = 0; i < crthits.size(); i++) { srcrthits.emplace_back(); - FillCRTHit(crthits[i], fParams.CRTUseTS0(), CRT_T0_reference_time, CRT_T1_reference_time, srcrthits.back()); + FillCRTHit(crthits[i], fParams.CRTUseTS0(), CRT_T0_reference_time, CRT_T1_reference_time, crtsimchanmap, srcrthits.back()); } } @@ -1619,12 +1857,15 @@ void CAFMaker::produce(art::Event& evt) noexcept { { art::Handle> crtspacepoints_handle; GetByLabelStrict(evt, fParams.CRTSpacePointLabel(), crtspacepoints_handle); + art::FindOneP foCRTCluster = + FindOnePStrict(crtspacepoints_handle, evt, fParams.CRTSpacePointLabel()); if (crtspacepoints_handle.isValid()) { const std::vector &crtspacepoints = *crtspacepoints_handle; for (unsigned i = 0; i < crtspacepoints.size(); i++) { srcrtspacepoints.emplace_back(); - FillCRTSpacePoint(crtspacepoints[i], srcrtspacepoints.back()); + const art::Ptr crtcluster = foCRTCluster.at(i); + FillCRTSpacePoint(crtspacepoints[i], *crtcluster, srcrtspacepoints.back()); } } @@ -1638,6 +1879,49 @@ void CAFMaker::produce(art::Event& evt) noexcept { FillSBNDCRTTrack(sbndcrttracks[i], srsbndcrttracks.back()); } } + + // Fill CRT Veto + art::Handle> sbndcrtveto_handle; + GetByLabelStrict(evt, fParams.SBNDCRTVetoLabel(), sbndcrtveto_handle); + // fill into event + if (sbndcrtveto_handle.isValid()) { + const std::vector &sbndcrtveto_vec = *sbndcrtveto_handle; + // Only one valid veto per event + if (sbndcrtveto_vec.size() == 1) { + // And associated SpacePoint objects + art::FindManyP spAssoc(sbndcrtveto_handle, evt, fParams.SBNDCRTVetoLabel()); + if (spAssoc.isValid()) { + // There is one vector of SpacePoints per Veto --> can be empty if no veto condition was satisfied + const std::vector>& veto_sp_v(spAssoc.at(0)); + FillSBNDCRTVeto(sbndcrtveto_vec[0], veto_sp_v, srsbndcrtveto); + } + } + } + + art::Handle sbndframeshiftinfo_handle; + GetByLabelStrict(evt, fParams.SBNDFrameShiftInfoLabel(), sbndframeshiftinfo_handle); + // fill into event + if (sbndframeshiftinfo_handle.isValid()) { + sbnd::timing::FrameShiftInfo const& sbndframeshiftinfo(*sbndframeshiftinfo_handle); + FillSBNDFrameShiftInfo(sbndframeshiftinfo, srsbndframeshiftinfo); + } + + art::Handle sbndtiminginfo_handle; + GetByLabelStrict(evt, fParams.SBNDTimingInfoLabel(), sbndtiminginfo_handle); + // fill into event + if (sbndtiminginfo_handle.isValid()) { + sbnd::timing::TimingInfo const& sbndtiminginfo(*sbndtiminginfo_handle); + FillSBNDTimingInfo(sbndtiminginfo, srsbndtiminginfo); + } + + art::Handle> sbndsofttrig_handle; + GetByLabelStrict(evt, fParams.SBNDSoftwareTriggerLabel(), sbndsofttrig_handle); + if (sbndsofttrig_handle.isValid()){ + const std::vector &sbndsofttrig = *sbndsofttrig_handle; + if (sbndsofttrig.size()==1){ + FillSoftwareTriggerSBND(sbndsofttrig.at(0), srsbndsofttrig); + } + } } // Get all of the CRTPMT Matches @@ -1654,30 +1938,72 @@ void CAFMaker::produce(art::Event& evt) noexcept { // Get all of the OpFlashes std::vector srflashes; + if(fDet == kICARUS) + { + //Get all of the special PMT Beam Signals (to use as an opFlash reference time below) + art::Handle> PMTBeamSignal_handle; + GetByLabelIfExists(evt, fParams.PMTBeamSignalLabel(), PMTBeamSignal_handle); + + for (const std::string& pandora_tag_suffix : pandora_tag_suffixes) { + art::Handle> flashes_handle; + GetByLabelStrict(evt, fParams.OpFlashLabel() + pandora_tag_suffix, flashes_handle); + // fill into event + if (flashes_handle.isValid()) { + const std::vector &opflashes = *flashes_handle; + int cryostat = ( pandora_tag_suffix.find("W") != std::string::npos ) ? 1 : 0; + + // get associated OpHits for each OpFlash + art::FindMany findManyHits(flashes_handle, evt, fParams.OpFlashLabel() + pandora_tag_suffix); + + int iflash=0; + for (const recob::OpFlash& flash : opflashes) { + + std::vector const& ophits = findManyHits.at(iflash); + + srflashes.emplace_back(); + if(PMTBeamSignal_handle.isValid() && isRealData){ + const std::vector &pmtbeamsignals = *PMTBeamSignal_handle; + FillICARUSOpFlash(flash, ophits, cryostat, pmtbeamsignals, srflashes.back()); + } + else{ + const std::vector pmtbeamsignals; + FillICARUSOpFlash(flash, ophits, cryostat, pmtbeamsignals, srflashes.back()); + } + iflash++; + } + } + } + } + else if(fDet == kSBND) + { + std::vector tpc_suffixes_sbnd = {"tpc0", "tpc1"}; - for (const std::string& pandora_tag_suffix : pandora_tag_suffixes) { - art::Handle> flashes_handle; - GetByLabelStrict(evt, fParams.OpFlashLabel() + pandora_tag_suffix, flashes_handle); - // fill into event - if (flashes_handle.isValid()) { - const std::vector &opflashes = *flashes_handle; - int cryostat = ( pandora_tag_suffix.find("W") != std::string::npos ) ? 1 : 0; - - // get associated OpHits for each OpFlash - art::FindMany findManyHits(flashes_handle, evt, fParams.OpFlashLabel() + pandora_tag_suffix); - - int iflash=0; - for (const recob::OpFlash& flash : opflashes) { - - std::vector const& ophits = findManyHits.at(iflash); - - srflashes.emplace_back(); - FillOpFlash(flash, ophits, cryostat, srflashes.back()); - iflash++; + for (size_t tpc=0; tpc> flashes_handle; + GetByLabelStrict(evt, fParams.OpFlashLabel() + tpc_suffixes_sbnd[tpc], flashes_handle); + // fill into event + if (flashes_handle.isValid()) { + const std::vector &opflashes = *flashes_handle; + // get associated OpHits for each OpFlash + art::FindMany findManyHits(flashes_handle, evt, fParams.OpFlashLabel() + tpc_suffixes_sbnd[tpc]); + int iflash=0; + for (const recob::OpFlash& flash : opflashes) { + std::vector const& ophits = findManyHits.at(iflash); + srflashes.emplace_back(); + FillSBNDOpFlash(flash, ophits, tpc, srflashes.back()); + iflash++; + } } } } + //Fill blips. art::handle for blips and then call fill blips for each one. Make a vector to hold all of them. I handle for loop in Fill blip + art::Handle> blipHandle; + std::vector srblips; + if(evt.getByLabel( fParams.fBlipTag(), blipHandle)) //fill SR blips + { + FillBlip( *blipHandle, srblips); + } // collect the TPC slices std::vector> slices; std::vector slice_tag_suffixes; @@ -1696,6 +2022,17 @@ void CAFMaker::produce(art::Event& evt) noexcept { } } + // nu graph + std::vector< art::Handle> > ng2_slice_hit_map_handle(pandora_tag_suffixes.size()); + std::vector< art::Handle>> > ng2_filter_handle(pandora_tag_suffixes.size()); + std::vector< art::Handle>> > ng2_semantic_handle(pandora_tag_suffixes.size()); + for (unsigned i_tag = 0; i_tag < pandora_tag_suffixes.size(); i_tag++) { + const std::string &pandora_tag_suffix = pandora_tag_suffixes[i_tag]; + GetByLabelIfExists(evt, fParams.NuGraphSliceHitLabel().encode() + pandora_tag_suffix, ng2_slice_hit_map_handle[i_tag]); + GetByLabelIfExists(evt, fParams.NuGraphFilterLabel().label() + pandora_tag_suffix + ":" + fParams.NuGraphFilterLabel().instance(), ng2_filter_handle[i_tag]); + GetByLabelIfExists(evt, fParams.NuGraphSemanticLabel().label() + pandora_tag_suffix + ":" + fParams.NuGraphSemanticLabel().instance(), ng2_semantic_handle[i_tag]); + } + // The Standard Record // Branch entry definition -- contains list of slices, CRT information, and truth information StandardRecord rec; @@ -1752,6 +2089,18 @@ void CAFMaker::produce(art::Event& evt) noexcept { } } + std::vector>> ng2_filter_vec; + std::vector>> ng2_semantic_vec; + if (ng2_filter_handle[producer].isValid()) { + art::fill_ptr_vector(ng2_filter_vec,ng2_filter_handle[producer]); + } + if (ng2_semantic_handle[producer].isValid()) { + art::fill_ptr_vector(ng2_semantic_vec,ng2_semantic_handle[producer]); + } + if (ng2_slice_hit_map_handle[producer].isValid()) { + FillSliceNuGraph(slcHits,*ng2_slice_hit_map_handle[producer],ng2_filter_vec,ng2_semantic_vec,recslc); + } + art::FindManyP fmOpT0 = FindManyPStrict(sliceList, evt, fParams.OpT0Label() + slice_tag_suff); std::vector> slcOpT0; @@ -1768,6 +2117,19 @@ void CAFMaker::produce(art::Event& evt) noexcept { const sbn::TPCPMTBarycenterMatch *barycenterMatch = foTPCPMTBarycenterMatch.isValid()? foTPCPMTBarycenterMatch.at(0).get(): nullptr; + + art::FindManyP fmCorrectedOpFlash = + FindManyPStrict(sliceList, evt, + fParams.CorrectedOpFlashLabel() + slice_tag_suff); + std::vector> slcCorrectedOpFlash; + if (fmCorrectedOpFlash.isValid()) + slcCorrectedOpFlash = fmCorrectedOpFlash.at(0); + + art::FindOneP foLightCalo = + FindOnePStrict(sliceList,evt, + fParams.LightCaloLabel() + slice_tag_suff); + const sbn::LightCalo *slcLightCalo = foLightCalo.isValid()? foLightCalo.at(0).get() : nullptr; + art::FindOneP foCVNResult = FindOnePStrict(sliceList, evt, fParams.CVNLabel() + slice_tag_suff); @@ -1798,6 +2160,25 @@ void CAFMaker::produce(art::Event& evt) noexcept { art::FindManyP fmSpacePointPFPs = FindManyPStrict(slcSpacePoints, evt, fParams.PFParticleLabel() + slice_tag_suff); + art::FindManyP fmPFPClusters = + FindManyPStrict(fmPFPart, evt, fParams.PFParticleLabel() + slice_tag_suff); + + std::vector>> fmPFPartHits; + // make Ptr's to clusters for cluster -> other object associations + if (fmPFPClusters.isValid()) { + for (size_t ipf=0; ipf> pfphits; + std::vector> pfclusters = fmPFPClusters.at(ipf); + art::FindManyP fmCluHits = FindManyPStrict(pfclusters, evt, fParams.PFParticleLabel() + slice_tag_suff); + for (size_t icl=0; icl fmShower = FindManyPStrict(fmPFPart, evt, fParams.RecoShowerLabel() + slice_tag_suff); @@ -1878,6 +2259,10 @@ void CAFMaker::produce(art::Event& evt) noexcept { FindManyPStrict(slcTracks, evt, fParams.TrackChi2PidLabel() + slice_tag_suff); + art::FindManyP fmLikePID = + FindManyPStrict(slcTracks, evt, + fParams.TrackLikePidLabel() + slice_tag_suff); + art::FindManyP fmScatterClosestApproach = FindManyPStrict(slcTracks, evt, fParams.TrackScatterClosestApproachLabel() + slice_tag_suff); @@ -1916,9 +2301,14 @@ void CAFMaker::produce(art::Event& evt) noexcept { // NOTE: The sbn::crt::CRTHit is associated to the T0. It's a bit awkward to // access that here, so we do it per-track (see code where fmCRTHitMatch is accessed below) + art::FindManyP fmCRTHitMatch = FindManyPStrict(slcTracks, evt, - fParams.CRTHitMatchLabel() + slice_tag_suff); + fParams.CRTHitMatchLabel()); + + art::FindManyP fmCRTHitMatchInfo = + FindManyPStrict(slcTracks, evt, + fParams.CRTHitMatchInfoLabel()); // TODO: also save the sbn::crt::CRTTrack in the matching so that CAFMaker has access to it art::FindManyP fmCRTTrackMatch = @@ -1929,6 +2319,9 @@ void CAFMaker::produce(art::Event& evt) noexcept { FindOnePDStrict(slcTracks, evt, fParams.CRTSpacePointMatchLabel() + slice_tag_suff); + art::Handle> crtspacepoints_handle; + GetByLabelStrict(evt, fParams.CRTSpacePointLabel(), crtspacepoints_handle); + art::FindOneP foSBNDCRTTrackMatch = FindOnePDStrict(slcTracks, evt, fParams.SBNDCRTTrackMatchLabel() + slice_tag_suff); @@ -1998,7 +2391,9 @@ void CAFMaker::produce(art::Event& evt) noexcept { FillSliceCRUMBS(slcCRUMBS, recslc); FillSliceOpT0Finder(slcOpT0, recslc); FillSliceBarycenter(slcHits, slcSpacePoints, recslc); + FillSliceLightCalo(slcLightCalo, recslc); FillTPCPMTBarycenterMatch(barycenterMatch, recslc); + FillCorrectedOpFlashTiming(slcCorrectedOpFlash, recslc); FillCVNScores(cvnResult, recslc); // select slice @@ -2098,13 +2493,34 @@ void CAFMaker::produce(art::Event& evt) noexcept { } const larpandoraobj::PFParticleMetadata *pfpMeta = (fmPFPMeta.at(iPart).empty()) ? NULL : fmPFPMeta.at(iPart).at(0).get(); - FillPFPVars(thisParticle, primary, pfpMeta, thisPFPT0, pfp); + caf::CAFMakerParams::PFOCharLabels_t const& pfoCharParams + = fParams.PFOCharLabels().value_or(caf::CAFMakerParams::PFOCharLabels_t{}); + const caf::PFOCharLabelsStruct pfoCharLabels { + pfoCharParams.EndFractionName(), + pfoCharParams.FractionalSpreadName(), + pfoCharParams.DiffStraightLineMeanName(), + pfoCharParams.LengthName(), + pfoCharParams.MaxFitGapLengthName(), + pfoCharParams.SlidingLinearFitRMSName(), + pfoCharParams.AngleDiffName(), + pfoCharParams.SecondaryPCARatioName(), + pfoCharParams.TertiaryPCARatioName(), + pfoCharParams.VertexDistanceName(), + pfoCharParams.HaloTotalRatioName(), + pfoCharParams.ConcentrationName(), + pfoCharParams.ConicalnessName() + }; + FillPFPVars(thisParticle, primary, pfpMeta, thisPFPT0, pfp, pfoCharLabels); if (fmCNNScores.isValid()) { const sbn::PFPCNNScore *cnnScores = fmCNNScores.at(iPart).at(0).get(); FillCNNScores(thisParticle, cnnScores, pfp); } + if (ng2_slice_hit_map_handle[producer].isValid()) { + FillPFPNuGraph(*ng2_slice_hit_map_handle[producer], ng2_filter_vec, ng2_semantic_vec, fmPFPartHits.at(iPart), pfp); + } + if (!thisTrack.empty()) { // it has a track! assert(thisTrack.size() == 1); @@ -2142,6 +2558,9 @@ void CAFMaker::produce(art::Event& evt) noexcept { if (fmChi2PID.isValid()) { FillTrackChi2PID(fmChi2PID.at(iPart), trk); } + if (fmLikePID.isValid()) { + FillTrackLikePID(fmLikePID.at(iPart), trk); + } if (fmScatterClosestApproach.isValid() && fmScatterClosestApproach.at(iPart).size()==1) { FillTrackScatterClosestApproach(fmScatterClosestApproach.at(iPart).front(), trk); } @@ -2157,14 +2576,19 @@ void CAFMaker::produce(art::Event& evt) noexcept { fParams.TrackHitFillRRStartCut(), fParams.TrackHitFillRREndCut(), dprop, trk); } + if (fmCRTHitMatch.isValid() && fDet == kICARUS) { art::FindManyP CRTT02Hit = FindManyPStrict - (fmCRTHitMatch.at(iPart), evt, fParams.CRTHitMatchLabel() + slice_tag_suff); + (fmCRTHitMatch.at(iPart), evt, fParams.CRTHitMatchLabel()); std::vector> crthitmatch; - if (CRTT02Hit.isValid() && CRTT02Hit.size() == 1) crthitmatch = CRTT02Hit.at(0); - - FillTrackCRTHit(fmCRTHitMatch.at(iPart), crthitmatch, fParams.CRTUseTS0(), CRT_T0_reference_time, CRT_T1_reference_time, trk); + std::vector> crthittagginginfo; + if(CRTT02Hit.isValid() && CRTT02Hit.size() == 1){ + crthitmatch = CRTT02Hit.at(0); + crthittagginginfo = fmCRTHitMatchInfo.at(iPart); + } + + FillTrackCRTHit(fmCRTHitMatch.at(iPart), crthitmatch, crthittagginginfo, fParams.CRTUseTS0(), CRT_T0_reference_time, CRT_T1_reference_time, crtsimchanmap, trk); } // NOTE: SEE TODO AT fmCRTTrackMatch if (fmCRTTrackMatch.isValid() && fDet == kICARUS) { @@ -2175,8 +2599,12 @@ void CAFMaker::produce(art::Event& evt) noexcept { { const art::Ptr crtspacepoint = foCRTSpacePointMatch.at(iPart); + art::FindOneP foCRTCluster = + FindOnePStrictSingle(crtspacepoint, evt, fParams.CRTSpacePointLabel()); + const art::Ptr& crtcluster = foCRTCluster.at(0); + if(crtspacepoint.isNonnull()) - FillTrackCRTSpacePoint(foCRTSpacePointMatch.data(iPart).ref(), crtspacepoint, trk); + FillTrackCRTSpacePoint(foCRTSpacePointMatch.data(iPart).ref(), *crtspacepoint, *crtcluster, trk); } if(foSBNDCRTTrackMatch.isValid() && fDet == kSBND) { @@ -2202,7 +2630,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { assert(thisShower.size() == 1); SRShower& shw = pfp.shw; - FillShowerVars(*thisShower[0], vertex, fmShowerHit.at(iPart), wireReadout, producer, shw); + FillShowerVars(*thisShower[0], vertex, fmShowerHit.at(iPart), wireReadout, producer, shw, fDet); // We may have many residuals per shower depending on how many showers ar in the slice if (fmShowerRazzle.isValid() && fmShowerRazzle.at(iPart).size()==1) { @@ -2262,8 +2690,14 @@ void CAFMaker::produce(art::Event& evt) noexcept { rec.ncrt_spacepoints = srcrtspacepoints.size(); rec.sbnd_crt_tracks = srsbndcrttracks; rec.nsbnd_crt_tracks = srsbndcrttracks.size(); + rec.sbnd_crt_veto = srsbndcrtveto; rec.opflashes = srflashes; rec.nopflashes = srflashes.size(); + rec.blips = srblips; + rec.sbnd_frames = srsbndframeshiftinfo; + rec.sbnd_timings = srsbndtiminginfo; + rec.soft_trig = srsbndsofttrig; + if (fParams.FillTrueParticles()) { rec.true_particles = true_particles; } @@ -2271,7 +2705,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { rec.crtpmt_matches = srcrtpmtmatches; rec.ncrtpmt_matches = srcrtpmtmatches.size(); - // Fix the Reference time + // Move the reference time of reconstructed objects from trigger time to beam spill/beam gate opening time. // // We want MC and Data to have the same reference time. // In MC/LArSoft the "reference time" is canonically defined @@ -2288,6 +2722,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { // filled with the default values, which are set to the numerical limits of double. // In this case, we should set the PMT_reference_time to 0. + // ICARUS: Fix the Reference time const bool hasValidTriggerTime = srtrigger.global_trigger_det_time > (std::numeric_limits::min() + std::numeric_limits::epsilon()) && @@ -2303,6 +2738,13 @@ void CAFMaker::produce(art::Event& evt) noexcept { FixPMTReferenceTimes(rec, PMT_reference_time); // TODO: TPC? + + + if (isRealData && (fDet == kSBND)) + { + // Fill trigger info + FillTriggerSBND(srsbndtiminginfo, srtrigger); + } // Get metadata information for header unsigned int run = evt.run(); @@ -2336,6 +2778,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { rec.hdr.ismc = !isRealData; rec.hdr.det = fDet; rec.hdr.fno = fFileNumber; + if(fFirstInSubRun) { rec.hdr.nbnbinfo = fBNBInfo.size(); @@ -2376,13 +2819,13 @@ void CAFMaker::produce(art::Event& evt) noexcept { else { std::cout << "Did not find this event in the spill info map." << std::endl; } - + if(fRecTree){ // Save the standard-record StandardRecord* prec = &rec; + fRecTree->SetBranchAddress("rec", &prec); fRecTree->Fill(); - if(fFlatTree){ fFlatRecord->Clear(); fFlatRecord->Fill(rec); @@ -2464,11 +2907,11 @@ void CAFMaker::endSubRun(art::SubRun& sr) { //...................................................................... void CAFMaker::AddHistogramsToFile(TFile* outfile,bool isBlindPOT = false, bool isPrescalePOT = false) const { - outfile->cd(); TH1* hPOT = new TH1D("TotalPOT", "TotalPOT;; POT", 1, 0, 1); TH1* hEvents = new TH1D("TotalEvents", "TotalEvents;; Events", 1, 0, 1); + TH1* hGen = new TH1D("TotalGenEvents", "TotalGenEvents;; Events", 1, 0, 1); if (isBlindPOT) { hPOT->Fill(0.5,fTotalPOT*(1-(1/fParams.PrescaleFactor()))*GetBlindPOTScale()); @@ -2480,13 +2923,15 @@ void CAFMaker::endSubRun(art::SubRun& sr) { hPOT->Fill(0.5,fTotalPOT); } hEvents->Fill(0.5,fTotalEvents); + hGen->Fill(0.5,fTotalGenEvents); hPOT->Write(); hEvents->Write(); + hGen->Write(); if (fParams.CreateBlindedCAF()) { TH1*hBlindEvents = new TH1D("BlindEvents", "BlindEvents;; Events", 1, 0, 1); - TH1* hPrescaleEvents = new TH1D("PrescaleEvents", "PrescaleEvents;; Events", 1, 0, 1); + TH1*hPrescaleEvents = new TH1D("PrescaleEvents", "PrescaleEvents;; Events", 1, 0, 1); hBlindEvents->Fill(0.5, fBlindEvents); hPrescaleEvents->Fill(0.5, fPrescaleEvents); hBlindEvents->Write(); @@ -2496,29 +2941,26 @@ void CAFMaker::endSubRun(art::SubRun& sr) { //...................................................................... void CAFMaker::endJob() { - if (fTotalEvents == 0) { - std::cerr << "No events processed in this file. Aborting rather than " - "produce an empty CAF." + // Only produce empty recTree/GenieTree since it relies on non-zero art events. + // Still want to keep POT histograms. + if (fTotalEvents == 0) { + std::cerr << "No events processed in this file. Producing empty recTree/GenieTree." << std::endl; - // n.b. changed abort() to return so that eny exceptions thrown during startup - // still get printed to the user by art - return; } - - if(fFile){ - AddHistogramsToFile(fFile); - fRecTree->SetDirectory(fFile); - if(fGenieTree){ - fGenieTree->BuildIndex("SourceFileHash", "GENIEEntry"); - fGenieTree->SetDirectory(fFile); - } - if (fParams.CreateBlindedCAF()) { - fRecTreeb->SetDirectory(fFileb); - fRecTreep->SetDirectory(fFilep); + if (fTotalEvents > 0) { + if(fGenieTree){ + fGenieTree->BuildIndex("SourceFileHash", "GENIEEntry"); + fGenieTree->SetDirectory(fFile); + } + fRecTree->SetDirectory(fFile); + if (fParams.CreateBlindedCAF()) { + fRecTreeb->SetDirectory(fFileb); + fRecTreep->SetDirectory(fFilep); + } } fFile->cd(); fFile->Write(); @@ -2534,17 +2976,19 @@ void CAFMaker::endJob() { } if(fFlatFile){ - AddHistogramsToFile(fFlatFile); - fFlatTree->SetDirectory(fFlatFile); - if(fFlatGenieTree){ - fFlatGenieTree->BuildIndex("SourceFileHash", "GENIEEntry"); - fFlatGenieTree->SetDirectory(fFlatFile); - } - if (fParams.CreateBlindedCAF() && fFlatFileb) { - fFlatTreeb->SetDirectory(fFlatFileb); - fFlatTreep->SetDirectory(fFlatFilep); + if (fTotalEvents > 0) { + if(fFlatGenieTree){ + fFlatGenieTree->BuildIndex("SourceFileHash", "GENIEEntry"); + fFlatGenieTree->SetDirectory(fFlatFile); + } + fFlatTree->SetDirectory(fFlatFile); + if (fParams.CreateBlindedCAF() && fFlatFileb) { + fFlatTreeb->SetDirectory(fFlatFileb); + fFlatTreep->SetDirectory(fFlatFilep); + } } + fFlatFile->cd(); fFlatFile->Write(); if (fParams.CreateBlindedCAF()) { @@ -2587,7 +3031,6 @@ void CAFMaker::endJob() { if(fParams.CreateBlindedCAF() && fFlatFilep) AddMetadataToFile(fFlatFilep, metamap); } - } // end namespace caf DEFINE_ART_MODULE(caf::CAFMaker) //////////////////////////////////////////////////////////////////////// diff --git a/sbncode/CAFMaker/CMakeLists.txt b/sbncode/CAFMaker/CMakeLists.txt index 76c3903d4..4368ff48a 100644 --- a/sbncode/CAFMaker/CMakeLists.txt +++ b/sbncode/CAFMaker/CMakeLists.txt @@ -25,6 +25,7 @@ art_make_library( LIBRARY_NAME sbncode_CAFMaker caf_RecoUtils lardataobj::AnalysisBase lardataobj::RecoBase + lardataobj::Simulation larrecodnn::CVN_func larcorealg::Geometry larcore::Geometry_Geometry_service @@ -35,21 +36,27 @@ art_make_library( LIBRARY_NAME sbncode_CAFMaker sbnobj::Common_CRT sbnobj::Common_Reco sbnobj::Common_Analysis + sbnobj::Common_PMT_Data + sbnobj::Common_Trigger sbnobj::SBND_CRT + sbnobj::SBND_Timing + sbnobj::SBND_Blip lardataalg::DetectorInfo art::Framework_Services_System_TriggerNamesService_service sbncode_Metadata_MetadataSBN_service larsim::Utils - larevt::SpaceCharge + larevt::SpaceCharge systematicstools::interface systematicstools::interpreters systematicstools::utility ${GENIE_LIB_LIST} nugen::EventGeneratorBase_GENIE + sbndaq_artdaq_core::sbndaq-artdaq-core_Obj_SBND + sbncode::sbn_POTTools ) cet_build_plugin ( CAFMaker art::module - LIBRARIES + LIBRARIES PRIVATE sbncode_CAFMaker sbnanaobj::StandardRecord larcorealg::Geometry diff --git a/sbncode/CAFMaker/FillBlip.cxx b/sbncode/CAFMaker/FillBlip.cxx new file mode 100644 index 000000000..5d71edb35 --- /dev/null +++ b/sbncode/CAFMaker/FillBlip.cxx @@ -0,0 +1,96 @@ +#include "sbncode/CAFMaker/FillBlip.h" + +namespace caf +{ + void FillBlip( const std::vector& LAr_Blips, std::vector& CAF_Blips) + { + for(blip::Blip const& CurrentBlip: LAr_Blips) + { + caf::SRBlip NewBlip; + NewBlip.ID = CurrentBlip.ID; + NewBlip.isValid = CurrentBlip.isValid; + NewBlip.cryostat = CurrentBlip.Cryostat; + NewBlip.TPC = CurrentBlip.TPC; + NewBlip.nPlanes = CurrentBlip.NPlanes; + NewBlip.maxWireSpan = CurrentBlip.MaxWireSpan; + NewBlip.timeTick = CurrentBlip.TimeTick; + NewBlip.time = CurrentBlip.Time; + NewBlip.charge = CurrentBlip.Charge; + NewBlip.energy = CurrentBlip.Energy/1000.; //convert to GeV + NewBlip.energyESTAR = CurrentBlip.EnergyESTAR/1000.; //convert to GeV + NewBlip.energyPSTAR = CurrentBlip.EnergyPSTAR/1000.; //convert to GeV + NewBlip.proxTrkDist = CurrentBlip.ProxTrkDist; + NewBlip.proxTrkID = CurrentBlip.ProxTrkID; + NewBlip.inCylinder = CurrentBlip.inCylinder; + NewBlip.position.SetXYZ(CurrentBlip.Position.X(), CurrentBlip.Position.Y(), CurrentBlip.Position.Z()); + NewBlip.sigmaYZ = CurrentBlip.SigmaYZ; + NewBlip.dX = CurrentBlip.dX; + NewBlip.dYZ = CurrentBlip.dYZ; + if(CurrentBlip.truth.ID >= 0 ) //MC Blip + { + FillMCTruthBlip( CurrentBlip.truth, NewBlip.truthBlip ); + } + for(int iPlane=0; iPlane + +namespace caf +{ + + void FillBlip( const std::vector& LAr_Blips, std::vector& CAF_Blips); + void FillMCTruthBlip( blip::TrueBlip const & TrueLAr_Blip, caf::SRTrueBlip &TrueCAF_Blip ); + void FillBlipRelatedHitCluster(blip::HitClust const & LAr_HitClust, caf::SRBlipHitClust &CAF_HitClust); +} + +#endif diff --git a/sbncode/CAFMaker/FillExposure.cxx b/sbncode/CAFMaker/FillExposure.cxx index 01dd11b80..b6bd758a9 100644 --- a/sbncode/CAFMaker/FillExposure.cxx +++ b/sbncode/CAFMaker/FillExposure.cxx @@ -15,14 +15,33 @@ namespace caf single_store.LM875A = info.LM875A; single_store.LM875B = info.LM875B; single_store.LM875C = info.LM875C; + single_store.HP873 = info.HP873; + single_store.VP873 = info.VP873; single_store.HP875 = info.HP875; single_store.VP875 = info.VP875; single_store.HPTG1 = info.HPTG1; single_store.VPTG1 = info.VPTG1; single_store.HPTG2 = info.HPTG2; single_store.VPTG2 = info.VPTG2; + single_store.FOM = info.FOM; + single_store.VP873 = info.VP873; + single_store.VP875Offset = info.VP875Offset; + single_store.VP873Offset = info.VP873Offset; + single_store.HP875Offset = info.HP875Offset; + single_store.VPTG1Offset = info.VPTG1Offset; + single_store.HPTG1Offset = info.HPTG1Offset; + single_store.VPTG2Offset = info.VPTG2Offset; + single_store.HPTG2Offset = info.HPTG2Offset; single_store.BTJT2 = info.BTJT2; single_store.THCURR = info.THCURR; + single_store.M875HS = info.M875HS; + single_store.M875VS = info.M875VS; + single_store.M875HM = info.M875HM; + single_store.M875VM = info.M875VM; + single_store.M876HS = info.M876HS; + single_store.M876VS = info.M876VS; + single_store.M876HM = info.M876HM; + single_store.M876VM = info.M876VM; single_store.M875BB = info.M875BB; single_store.M876BB = info.M876BB; single_store.MMBTBB = info.MMBTBB; diff --git a/sbncode/CAFMaker/FillReco.cxx b/sbncode/CAFMaker/FillReco.cxx index f47265c1e..77420f1bb 100644 --- a/sbncode/CAFMaker/FillReco.cxx +++ b/sbncode/CAFMaker/FillReco.cxx @@ -12,6 +12,7 @@ namespace caf { + const float ng_filter_cut = 0.5; //...................................................................... bool SelectSlice(const caf::SRSlice &slice, bool cut_clear_cosmic) { @@ -67,6 +68,7 @@ namespace caf bool use_ts0, int64_t CRT_T0_reference_time, // ns, signed double CRT_T1_reference_time, // us + const std::map, sim::AuxDetSimChannel> &crtsimchanmap, caf::SRCRTHit &srhit, bool allowEmpty) { @@ -85,6 +87,52 @@ namespace caf srhit.pe = hit.peshit; srhit.plane = hit.plane; + // lookup truth matching information + std::map true_energy; + std::set> checked; // keep track of what AuxDetSimChannels we have looked at + for (auto const &mac_to_pes: hit.pesmap) { + int mac = (int)mac_to_pes.first; + for (const std::pair &chan_pe: mac_to_pes.second) { + int chan = chan_pe.first; + + // check for the 3-pos Minos ("m") modules, or the 1-pos other modules + bool is_minos_module = mac < 94; + int pos = is_minos_module ? (chan/10 + 1) : 1; + + std::pair key {mac, pos}; + if (checked.count(key)) continue; // already looked here + + checked.insert(key); + + if (crtsimchanmap.count(key)) { + const sim::AuxDetSimChannel &crtsimchan = crtsimchanmap.at(key); + for (const sim::AuxDetIDE &ide: crtsimchan.AuxDetIDEs()) { + double hit_time_us = srhit.time; + double tru_time_us = ide.entryT/1e3; // ns -> us + + // 1us cut on truth matching + if (abs(hit_time_us - tru_time_us) < 1) { + // std::cout << "Hit at time: " << (srhit.time/1e3) << " " << (srhit.t0/1e3) << " " << (srhit.t1/1e3) << " pes: " << chan_pe.second << " of " << srhit.pe << " on FEB: " << mac << " channel: " << chan << " matched to AuxDetID: " << crtsimchan.AuxDetID() << " G4ID: " << ide.trackID << " E: " << ide.energyDeposited << " T: " << (ide.entryT/1e6) << std::endl; + true_energy[ide.trackID] += ide.energyDeposited; + } + + } + } + } + } + + // sort results by energy + std::vector> true_energy_list; + for (auto const &pair: true_energy) true_energy_list.push_back(std::make_pair(pair.second, pair.first)); + std::sort(true_energy_list.begin(), true_energy_list.end(), std::greater<>()); + + // Save to the SRCRTHit truth + for (auto const &pair: true_energy_list) { + srhit.truth.match_e.push_back(pair.first); + srhit.truth.match_id.push_back(pair.second); + } + if (true_energy_list.size() > 0) srhit.truth.bestmatch_id = true_energy_list[0].second; + } void FillCRTTrack(const sbn::crt::CRTTrack &track, @@ -116,15 +164,18 @@ namespace caf } void FillCRTSpacePoint(const sbnd::crt::CRTSpacePoint &spacepoint, + const sbnd::crt::CRTCluster &cluster, caf::SRCRTSpacePoint &srspacepoint, bool allowEmpty) { srspacepoint.position = SRVector3D(spacepoint.X(), spacepoint.Y(), spacepoint.Z()); srspacepoint.position_err = SRVector3D(spacepoint.XErr(), spacepoint.YErr(), spacepoint.ZErr()); srspacepoint.pe = spacepoint.PE(); - srspacepoint.time = spacepoint.Ts1(); - srspacepoint.time_err = spacepoint.Ts1Err(); + srspacepoint.time = spacepoint.Ts0(); + srspacepoint.time_err = spacepoint.Ts0Err(); srspacepoint.complete = spacepoint.Complete(); + srspacepoint.nhits = cluster.NHits(); + srspacepoint.tagger = cluster.Tagger(); } void FillSBNDCRTTrack(const sbnd::crt::CRTTrack &track, @@ -134,10 +185,68 @@ namespace caf for(auto const& point : track.Points()) srsbndcrttrack.points.emplace_back(point.X(), point.Y(), point.Z()); - srsbndcrttrack.time = track.Ts1(); - srsbndcrttrack.time_err = track.Ts1Err(); + srsbndcrttrack.time = track.Ts0(); + srsbndcrttrack.time_err = track.Ts0Err(); srsbndcrttrack.pe = track.PE(); srsbndcrttrack.tof = track.ToF(); + + for(auto const& tagger : track.Taggers()) + srsbndcrttrack.taggers.push_back(tagger); + } + + void FillSBNDCRTVeto(const sbnd::crt::CRTVeto &veto, + const std::vector> &points, + caf::SRSBNDCRTVeto &srsbndcrtveto, + bool allowEmpty) + { + srsbndcrtveto.V0 = veto.V0(); + srsbndcrtveto.V1 = veto.V1(); + srsbndcrtveto.V2 = veto.V2(); + srsbndcrtveto.V3 = veto.V3(); + srsbndcrtveto.V4 = veto.V4(); + + // add the CRTSpacePoint associations to the SR Veto + for(auto const& sp : points) { + srsbndcrtveto.sp_position.emplace_back(sp->X(), sp->Y(), sp->Z()); + srsbndcrtveto.sp_time.push_back(sp->Ts0()); // ns for SBND CRT SpacePoints + srsbndcrtveto.sp_pe.push_back(sp->PE()); + } + } + + void FillSBNDFrameShiftInfo(const sbnd::timing::FrameShiftInfo &frame, + caf::SRSBNDFrameShiftInfo &srsbndframe, + bool allowEmpty) + { + srsbndframe.frameCrtt1 = frame.FrameCrtt1(); + srsbndframe.timingTypeCrtt1 = frame.TimingTypeCrtt1(); + srsbndframe.timingChannelCrtt1 = frame.TimingChannelCrtt1(); + + srsbndframe.frameBeamGate = frame.FrameBeamGate(); + srsbndframe.timingTypeBeamGate = frame.TimingTypeBeamGate(); + srsbndframe.timingChannelBeamGate = frame.TimingChannelBeamGate(); + + srsbndframe.frameEtrig = frame.FrameEtrig(); + srsbndframe.timingTypeEtrig = frame.TimingTypeEtrig(); + srsbndframe.timingChannelEtrig = frame.TimingChannelEtrig(); + + srsbndframe.frameDefault = frame.FrameDefault(); + srsbndframe.timingTypeDefault = frame.TimingTypeDefault(); + srsbndframe.timingChannelDefault = frame.TimingChannelDefault(); + + } + + void FillSBNDTimingInfo(const sbnd::timing::TimingInfo &timing, + caf::SRSBNDTimingInfo &srsbndtiming, + bool allowEmpty) + { + srsbndtiming.rawDAQHeaderTimestamp = timing.RawDAQHeaderTimestamp(); + srsbndtiming.tdcCrtt1 = timing.TdcCrtt1(); + srsbndtiming.tdcBes = timing.TdcBes(); + srsbndtiming.tdcRwm = timing.TdcRwm(); + srsbndtiming.tdcEtrig = timing.TdcEtrig(); + srsbndtiming.hltCrtt1 = timing.HltCrtt1(); + srsbndtiming.hltEtrig = timing.HltEtrig(); + srsbndtiming.hltBeamGate = timing.HltBeamGate(); } void FillCRTPMTMatch(const sbn::crt::CRTPMTMatching &match, @@ -179,9 +288,10 @@ namespace caf } - void FillOpFlash(const recob::OpFlash &flash, + void FillICARUSOpFlash(const recob::OpFlash &flash, std::vector const& hits, int cryo, + std::vector RWMTimes, caf::SROpFlash &srflash, bool allowEmpty) { @@ -191,11 +301,15 @@ namespace caf srflash.timewidth = flash.TimeWidth(); double firstTime = std::numeric_limits::max(); + std::map risemap; for(const auto& hit: hits){ double const hitTime = hit->HasStartTime()? hit->StartTime(): hit->PeakTime(); if (firstTime > hitTime) firstTime = hitTime; + if (!RWMTimes.empty()) + sbn::timing::SelectFirstOpHitByTime(hit,risemap); } + srflash.rwmtime = getFlashBunchTime(risemap, RWMTimes); srflash.firsttime = firstTime; srflash.cryo = cryo; // 0 in SBND, 0/1 for E/W in ICARUS @@ -227,6 +341,56 @@ namespace caf } } + void FillSBNDOpFlash(const recob::OpFlash &flash, + std::vector const& hits, + int tpc, + caf::SROpFlash &srflash, + bool allowEmpty) { + + srflash.setDefault(); + + srflash.time = flash.Time(); + srflash.timewidth = flash.TimeWidth(); + + double firstTime = std::numeric_limits::max(); + for(const auto& hit: hits){ + double const hitTime = hit->HasStartTime()? hit->StartTime(): hit->PeakTime(); + if (firstTime > hitTime) + firstTime = hitTime; + } + srflash.firsttime = firstTime; + srflash.tpc = tpc; + + srflash.totalpe = flash.TotalPE(); + srflash.fasttototal = flash.FastToTotal(); + srflash.onbeamtime = flash.OnBeamTime(); + + srflash.center.SetXYZ( -9999.f, flash.YCenter(), flash.ZCenter() ); + srflash.width.SetXYZ( -9999.f, flash.YWidth(), flash.ZWidth() ); + + // Checks if ( recob::OpFlash.XCenter() != std::numeric_limits::max() ) + // See LArSoft OpFlash.h at https://nusoft.fnal.gov/larsoft/doxsvn/html/OpFlash_8h_source.html + if ( flash.hasXCenter() ) { + srflash.center.SetX( flash.XCenter() ); + srflash.width.SetX( flash.XWidth() ); + } + } + + + void FillCorrectedOpFlashTiming(const std::vector> &slcCorrectedOpFlash, + caf::SRSlice& slice) + { + slice.correctedOpFlash.setDefault(); + if ( slcCorrectedOpFlash.empty()==false ) { + const sbn::CorrectedOpFlashTiming &_correctedOpFlash = *slcCorrectedOpFlash[0]; + slice.correctedOpFlash.OpFlashT0 = _correctedOpFlash.OpFlashT0; + slice.correctedOpFlash.NuToFLight = _correctedOpFlash.NuToFLight; + slice.correctedOpFlash.NuToFCharge = _correctedOpFlash.NuToFCharge; + slice.correctedOpFlash.OpFlashT0Corrected = _correctedOpFlash.OpFlashT0Corrected; + } + } + + std::vector double_to_float_vector(const std::vector& v) { std::vector ret; @@ -242,6 +406,7 @@ namespace caf const geo::WireReadoutGeom& wireReadout, unsigned producer, caf::SRShower &srshower, + Det_t det, bool allowEmpty) { @@ -262,11 +427,56 @@ namespace caf // It's sth like this but not quite. And will need to pass a simb::MCtruth object vtx position anyway. // srshower.conversion_gap = (shower.ShowerStart() - vertex.Position()).Mag(); - if(shower.best_plane() != -999){ - srshower.bestplane = shower.best_plane(); - srshower.bestplane_dEdx = srshower.plane[shower.best_plane()].dEdx; - srshower.bestplane_energy = srshower.plane[shower.best_plane()].energy; - } + for(int p = 0; p < 3; ++p) srshower.plane[p].nHits = 0; + for (auto const& hit:hits) ++srshower.plane[hit->WireID().Plane].nHits; + + if(det == kSBND) + { + int bestplane_for_energy = -999; + int mosthits = -1; + for(int p = 0; p < 3; ++p) + { + if((int)srshower.plane[p].nHits > mosthits) + { + mosthits = srshower.plane[p].nHits; + bestplane_for_energy = p; + } + } + + if(bestplane_for_energy != -999) + { + srshower.bestplane_for_energy = bestplane_for_energy; + srshower.bestplane_energy = srshower.plane[bestplane_for_energy].energy; + } + + if(shower.best_plane() != -999 && srshower.plane[shower.best_plane()].dEdx != -999) + { + srshower.bestplane_for_dedx = shower.best_plane(); + srshower.bestplane_dEdx = srshower.plane[shower.best_plane()].dEdx; + } + else + { + for(int p = 2; p >= 0; --p) + { + if(srshower.plane[p].dEdx != -999) + { + srshower.bestplane_for_dedx = p; + srshower.bestplane_dEdx = srshower.plane[shower.best_plane()].dEdx; + break; + } + } + } + } + else + { + if(shower.best_plane() != -999) + { + srshower.bestplane_for_energy = shower.best_plane(); + srshower.bestplane_for_dedx = shower.best_plane(); + srshower.bestplane_dEdx = srshower.plane[shower.best_plane()].dEdx; + srshower.bestplane_energy = srshower.plane[shower.best_plane()].energy; + } + } if(shower.has_open_angle()) srshower.open_angle = shower.OpenAngle(); @@ -289,9 +499,6 @@ namespace caf srshower.end = shower.ShowerStart()+ (shower.Length() * shower.Direction()); } - for(int p = 0; p < 3; ++p) srshower.plane[p].nHits = 0; - for (auto const& hit:hits) ++srshower.plane[hit->WireID().Plane].nHits; - for (geo::PlaneGeo const& plane: wireReadout.Iterate()) { const double angleToVert(wireReadout.WireAngleToVertical(plane.View(), plane.ID()) - 0.5*M_PI); @@ -501,6 +708,17 @@ namespace caf } } + void FillSliceLightCalo(const sbn::LightCalo *lightcalo, + caf::SRSlice &slice) + { + if (lightcalo != nullptr) { + slice.lightcalo.charge = lightcalo->charge; + slice.lightcalo.light = lightcalo->light; + slice.lightcalo.energy = lightcalo->energy; + slice.lightcalo.bestplane = lightcalo->bestplane; + } + } + void FillSliceBarycenter(const std::vector> &inputHits, const std::vector> &inputPoints, caf::SRSlice &slice) @@ -544,23 +762,62 @@ namespace caf } } + void FillSliceNuGraph(const std::vector> &inputHits, + const std::vector &sliceHitsMap, + const std::vector>> &ngFilterResult, + const std::vector>> &ngSemanticResult, + caf::SRSlice &slice) + { + + //need to double check that the slice processed by NuGraph is the same under consideration + //std::cout << "sizes=" << inputHits.size() << " " << sliceHitsMap.size() << " " << ngFilterResult.size() << " " << ngSemanticResult.size() << std::endl; + unsigned int nHits = inputHits.size(); + if (nHits==0 || nHits!=sliceHitsMap.size() || inputHits[0].key()!=sliceHitsMap[0]) return;//not the same slice! + + unsigned int npass = 0; + for ( unsigned int i = 0; i < nHits; i++ ) { + if (ngFilterResult.at(i)->at(0)>=ng_filter_cut) npass++; + } + slice.ng_filt_pass_frac = float(npass)/nHits; + } + //...................................................................... + void FillTrackCRTHit(const std::vector> &t0match, const std::vector> &hitmatch, + const std::vector> &hitmatchinfo, bool use_ts0, int64_t CRT_T0_reference_time, // ns, signed double CRT_T1_reference_time, // us + const std::map, sim::AuxDetSimChannel> &crtsimchanmap, caf::SRTrack &srtrack, bool allowEmpty) { + // Francesco Poppi: In the current implementation, hitmatch and hitmatchinfo are + // vectors of pointers, but currently they are vector of size 1. + // The reason behind the current implementation is that we only store the + // best CRT candidate in the selection, eventually, we can store a vector + // of "good" candidates and fill additional infos for all of them. + // This is a TODO. if (t0match.size()) { assert(t0match.size() == 1); srtrack.crthit.distance = t0match[0]->fTriggerConfidence; + srtrack.crthit.region = t0match[0]->fID; + srtrack.crthit.sys = t0match[0]->fTriggerBits; + if(hitmatchinfo.size() == 1){ + srtrack.crthit.deltaX = hitmatchinfo[0]->DeltaX; + srtrack.crthit.deltaY = hitmatchinfo[0]->DeltaY; + srtrack.crthit.deltaZ = hitmatchinfo[0]->DeltaZ; + srtrack.crthit.crossX = hitmatchinfo[0]->CrossX; + srtrack.crthit.crossY = hitmatchinfo[0]->CrossY; + srtrack.crthit.crossZ = hitmatchinfo[0]->CrossZ; + } srtrack.crthit.hit.time = t0match[0]->fTime / 1e3; /* ns -> us */ + srtrack.crthit.hit.plane = t0match[0]->fID; } if (hitmatch.size()) { - FillCRTHit(*hitmatch[0], use_ts0, CRT_T0_reference_time, CRT_T1_reference_time, srtrack.crthit.hit, allowEmpty); + FillCRTHit(*hitmatch[0], use_ts0, CRT_T0_reference_time, CRT_T1_reference_time, crtsimchanmap, srtrack.crthit.hit, allowEmpty); } } @@ -579,12 +836,15 @@ namespace caf } void FillTrackCRTSpacePoint(const anab::T0 &t0match, - const art::Ptr &spacepointmatch, + const sbnd::crt::CRTSpacePoint &spacepointmatch, + const sbnd::crt::CRTCluster &cluster, caf::SRTrack &srtrack, bool allowEmpty) { - srtrack.crtspacepoint.score = t0match.fTriggerConfidence; - FillCRTSpacePoint(*spacepointmatch, srtrack.crtspacepoint.spacepoint); + srtrack.crtspacepoint.matched = true; + srtrack.crtspacepoint.score = t0match.fTriggerConfidence; + + FillCRTSpacePoint(spacepointmatch, cluster, srtrack.crtspacepoint.spacepoint); } void FillTrackSBNDCRTTrack(const anab::T0 &t0match, @@ -592,7 +852,9 @@ namespace caf caf::SRTrack &srtrack, bool allowEmpty) { - srtrack.crtsbndtrack.score = t0match.fTriggerConfidence; + srtrack.crtsbndtrack.matched = true; + srtrack.crtsbndtrack.score = t0match.fTriggerConfidence; + FillSBNDCRTTrack(*trackmatch, srtrack.crtsbndtrack.track); } @@ -720,6 +982,48 @@ namespace caf } } + void FillPlaneLikePID(const anab::ParticleID &particle_id, caf::SRTrkLikelihoodPID &srlikepid) { + + // Loop over algorithm scores and extract the ones we want. + // Get the ndof from any likelihood pid algorithm + srlikepid.setDefault(); + + std::vector const& AlgScoresVec = particle_id.ParticleIDAlgScores(); + for (anab::sParticleIDAlgScores const& AlgScore: AlgScoresVec){ + if (AlgScore.fAlgName == "Likelihood"){ + switch (std::abs(AlgScore.fAssumedPdg)) { + case 13: // lambda_mu + srlikepid.lambda_muon = AlgScore.fValue; + srlikepid.pid_ndof = AlgScore.fNdf; + break; + case 211: // lambda_pi + srlikepid.lambda_pion = AlgScore.fValue; + srlikepid.pid_ndof = AlgScore.fNdf; + break; + case 2212: // lambda_pr + srlikepid.lambda_proton = AlgScore.fValue; + srlikepid.pid_ndof = AlgScore.fNdf; + break; + } + } + } + } + + void FillTrackLikePID(const std::vector>& particleIDs, + caf::SRTrack& srtrack, + bool allowEmpty) + { + // get the particle ID's + for (art::Ptr const& pidPtr: particleIDs) { + const anab::ParticleID &particle_id = *pidPtr; + if (particle_id.PlaneID()) { + unsigned plane_id = particle_id.PlaneID().Plane; + assert(plane_id < 3); + FillPlaneLikePID(particle_id, srtrack.likepid[plane_id]); + } + } + } + void FillTrackPlaneCalo(const anab::Calorimetry &calo, const std::vector> &hits, bool fill_calo_points, float fillhit_rrstart, float fillhit_rrend, @@ -731,6 +1035,8 @@ namespace caf const std::vector &dedx = calo.dEdx(); const std::vector &pitch = calo.TrkPitchVec(); const std::vector &rr = calo.ResidualRange(); + const std::vector &efield = calo.Efield(); + const std::vector &phi = calo.Phi(); const std::vector &xyz = calo.XYZ(); const std::vector &tps = calo.TpIndices(); @@ -752,6 +1058,8 @@ namespace caf p.dqdx = dqdx[i]; p.dedx = dedx[i]; p.pitch = pitch[i]; + p.efield = efield[i]; + p.phi = phi[i] * M_PI / 180.; // converting to radian since calo.Phi() is in degree p.x = xyz[i].x(); p.y = xyz[i].y(); p.z = xyz[i].z(); @@ -895,6 +1203,7 @@ namespace caf const larpandoraobj::PFParticleMetadata *pfpMeta, const art::Ptr t0, caf::SRPFP& srpfp, + const PFOCharLabelsStruct& pfoCharLabels, bool allowEmpty) { srpfp.id = particle.Self(); @@ -918,19 +1227,19 @@ namespace caf // Pfo Characterisation features srpfp.pfochar.setDefault(); - CopyPropertyIfSet(propertiesMap, "LArThreeDChargeFeatureTool_EndFraction", srpfp.pfochar.chgendfrac); - CopyPropertyIfSet(propertiesMap, "LArThreeDChargeFeatureTool_FractionalSpread", srpfp.pfochar.chgfracspread); - CopyPropertyIfSet(propertiesMap, "LArThreeDLinearFitFeatureTool_DiffStraightLineMean", srpfp.pfochar.linfitdiff); - CopyPropertyIfSet(propertiesMap, "LArThreeDLinearFitFeatureTool_Length", srpfp.pfochar.linfitlen); - CopyPropertyIfSet(propertiesMap, "LArThreeDLinearFitFeatureTool_MaxFitGapLength", srpfp.pfochar.linfitgaplen); - CopyPropertyIfSet(propertiesMap, "LArThreeDLinearFitFeatureTool_SlidingLinearFitRMS", srpfp.pfochar.linfitrms); - CopyPropertyIfSet(propertiesMap, "LArThreeDOpeningAngleFeatureTool_AngleDiff", srpfp.pfochar.openanglediff); - CopyPropertyIfSet(propertiesMap, "LArThreeDPCAFeatureTool_SecondaryPCARatio", srpfp.pfochar.pca2ratio); - CopyPropertyIfSet(propertiesMap, "LArThreeDPCAFeatureTool_TertiaryPCARatio", srpfp.pfochar.pca3ratio); - CopyPropertyIfSet(propertiesMap, "LArThreeDVertexDistanceFeatureTool_VertexDistance", srpfp.pfochar.vtxdist); - CopyPropertyIfSet(propertiesMap, "LArConeChargeFeatureTool_HaloTotalRatio", srpfp.pfochar.halototratio); - CopyPropertyIfSet(propertiesMap, "LArConeChargeFeatureTool_Concentration", srpfp.pfochar.concentration); - CopyPropertyIfSet(propertiesMap, "LArConeChargeFeatureTool_Conicalness", srpfp.pfochar.conicalness); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.EndFractionName, srpfp.pfochar.chgendfrac); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.FractionalSpreadName, srpfp.pfochar.chgfracspread); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.DiffStraightLineMeanName, srpfp.pfochar.linfitdiff); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.LengthName, srpfp.pfochar.linfitlen); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.MaxFitGapLengthName, srpfp.pfochar.linfitgaplen); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.SlidingLinearFitRMSName, srpfp.pfochar.linfitrms); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.AngleDiffName, srpfp.pfochar.openanglediff); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.SecondaryPCARatioName, srpfp.pfochar.pca2ratio); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.TertiaryPCARatioName, srpfp.pfochar.pca3ratio); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.VertexDistanceName, srpfp.pfochar.vtxdist); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.HaloTotalRatioName, srpfp.pfochar.halototratio); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.ConcentrationName, srpfp.pfochar.concentration); + CopyPropertyIfSet(propertiesMap, pfoCharLabels.ConicalnessName, srpfp.pfochar.conicalness); } if (t0) { srpfp.t0 = t0->Time() / 1e3; /* ns -> us */ @@ -950,6 +1259,59 @@ namespace caf srpfp.cnnscore.nclusters = cnnscore->nClusters; } + void FillPFPNuGraph(const std::vector &sliceHitsMap, + const std::vector>> &ngFilterResult, + const std::vector>> &ngSemanticResult, + const std::vector> &pfpHits, + caf::SRPFP& srpfp, + bool allowEmpty) + { + + // the nugraph elements are ordered the same as the sliceHitsMap + std::vector mappedhits; + for (auto& hit : pfpHits) { + auto it = std::find(sliceHitsMap.begin(), sliceHitsMap.end(), hit.key()); + if (it != sliceHitsMap.end()) { + size_t index = std::distance(sliceHitsMap.begin(), it); + mappedhits.push_back(index); + } + } + + if (mappedhits.size()>0) { + std::vector ng2sempfpcounts(5,0); + size_t ng2bkgpfpcount = 0; + for (size_t pos : mappedhits) { + auto const& bkgscore = ngFilterResult.at(pos); + if (bkgscore->at(0) ng2semscores; + for (size_t i=0;isize();i++) ng2semscores.push_back(scores->at(i)); + size_t sem_label = std::distance(ng2semscores.begin(), std::max_element(ng2semscores.begin(), ng2semscores.end()));//arg_max(ng2semscores); + ng2sempfpcounts[sem_label]++; + } + } + srpfp.ngscore.sem_cat = SRNuGraphScore::NuGraphCategory(std::distance(ng2sempfpcounts.begin(), std::max_element(ng2sempfpcounts.begin(), ng2sempfpcounts.end())));//arg_max(ng2sempfpcounts); + size_t nonBkgHits = (pfpHits.size() > ng2bkgpfpcount ? pfpHits.size()-ng2bkgpfpcount : 0); + srpfp.ngscore.mip_frac = (nonBkgHits>0 ? float(ng2sempfpcounts[0])/nonBkgHits : -1.); + srpfp.ngscore.hip_frac = (nonBkgHits>0 ? float(ng2sempfpcounts[1])/nonBkgHits : -1.); + srpfp.ngscore.shr_frac = (nonBkgHits>0 ? float(ng2sempfpcounts[2])/nonBkgHits : -1.); + srpfp.ngscore.mhl_frac = (nonBkgHits>0 ? float(ng2sempfpcounts[3])/nonBkgHits : -1.); + srpfp.ngscore.dif_frac = (nonBkgHits>0 ? float(ng2sempfpcounts[4])/nonBkgHits : -1.); + srpfp.ngscore.bkg_frac = float(ng2bkgpfpcount)/pfpHits.size(); + } else { + srpfp.ngscore.sem_cat = SRNuGraphScore::NuGraphCategory::Unset; + srpfp.ngscore.mip_frac = -1.; + srpfp.ngscore.hip_frac = -1.; + srpfp.ngscore.shr_frac = -1.; + srpfp.ngscore.mhl_frac = -1.; + srpfp.ngscore.dif_frac = -1.; + srpfp.ngscore.bkg_frac = -1.; + } + + } + void FillHitVars(const recob::Hit& hit, unsigned producer, const recob::SpacePoint& spacepoint, @@ -1000,6 +1362,8 @@ namespace caf slice.barycenterFM.deltaZ_Trigger = matchInfo->deltaZ_Trigger; slice.barycenterFM.deltaY_Trigger = matchInfo->deltaY_Trigger; slice.barycenterFM.radius_Trigger = matchInfo->radius_Trigger; + slice.barycenterFM.score = matchInfo->score; + slice.barycenterFM.chi2 = matchInfo->chi2; } } diff --git a/sbncode/CAFMaker/FillReco.h b/sbncode/CAFMaker/FillReco.h index c6df1ab39..a837e0b56 100644 --- a/sbncode/CAFMaker/FillReco.h +++ b/sbncode/CAFMaker/FillReco.h @@ -20,10 +20,12 @@ #include "lardataobj/RecoBase/OpFlash.h" #include "lardataobj/RecoBase/OpHit.h" #include "lardataobj/AnalysisBase/Calorimetry.h" +#include "lardataobj/AnalysisBase/MVAOutput.h" #include "lardataobj/AnalysisBase/ParticleID.h" #include "lardataobj/AnalysisBase/T0.h" #include "lardataobj/RecoBase/PFParticleMetadata.h" #include "lardataobj/RecoBase/MCSFitResult.h" +#include "lardataobj/Simulation/AuxDetSimChannel.h" #include "larrecodnn/CVN/func/Result.h" #include "sbnobj/Common/Reco/Stub.h" #include "sbnobj/Common/Reco/RangeP.h" @@ -35,11 +37,19 @@ #include "sbnobj/Common/Reco/CRUMBSResult.h" #include "sbnobj/Common/Reco/OpT0FinderResult.h" #include "sbnobj/Common/Reco/TPCPMTBarycenterMatch.h" +#include "sbnobj/Common/Reco/CorrectedOpFlashTiming.h" #include "sbnobj/Common/CRT/CRTHit.hh" #include "sbnobj/Common/CRT/CRTTrack.hh" #include "sbnobj/SBND/CRT/CRTSpacePoint.hh" +#include "sbnobj/SBND/CRT/CRTCluster.hh" #include "sbnobj/SBND/CRT/CRTTrack.hh" +#include "sbnobj/SBND/CRT/CRTVeto.hh" #include "sbnobj/Common/CRT/CRTPMTMatching.hh" +#include "sbnobj/Common/CRT/CRTHitT0TaggingInfo.hh" +#include "sbnobj/Common/PMT/Data/PMTBeamSignal.hh" +#include "sbnobj/SBND/Timing/TimingInfo.hh" +#include "sbnobj/SBND/Timing/FrameShiftInfo.hh" +#include "sbnobj/Common/Reco/LightCalo.h" #include "nusimdata/SimulationBase/MCParticle.h" #include "nusimdata/SimulationBase/MCTruth.h" @@ -61,6 +71,7 @@ namespace caf const geo::WireReadoutGeom& wireReadout, unsigned producer, caf::SRShower& srshower, + Det_t det, bool allowEmpty = false); void FillShowerRazzle(const art::Ptr razzle, @@ -100,10 +111,29 @@ namespace caf void FillSliceOpT0Finder(const std::vector> &opt0_v, caf::SRSlice &slice); + void FillSliceLightCalo(const sbn::LightCalo *lightcalo, + caf::SRSlice& slice); + void FillSliceBarycenter(const std::vector> &inputHits, const std::vector> &inputPoints, caf::SRSlice &slice); + /** + * @brief Fills the results from NuGraph at slice level + * @param inputHits (pointers to) the hits associated to the slice + * @param sliceHitsMap maps position of hits in collection input to NuGraph (slice only) to the one input to Pandora (all gaus hits) + * @param ngFilterResult NuGraph filter result, for each hit + * @param ngSemanticResult NuGraph semnatic result, for each hit (MIP track, HIP, shower, Michel electron, diffuse activity) + * @param[out] slice the destination slice object + * + * Hits with filter value (`ngFilterResult`) lower than `ng_filter_cut` are counted as background. + */ + void FillSliceNuGraph(const std::vector> &inputHits, + const std::vector &sliceHitsMap, + const std::vector>> &ngFilterResult, + const std::vector>> &ngSemanticResult, + caf::SRSlice &slice); + bool SelectSlice(const caf::SRSlice &slice, bool cut_clear_cosmic); void FillTrackVars(const recob::Track& track, @@ -118,23 +148,59 @@ namespace caf caf::SRHit& srhit, bool allowEmpty = false); + struct PFOCharLabelsStruct { + std::string EndFractionName; + std::string FractionalSpreadName; + std::string DiffStraightLineMeanName; + std::string LengthName; + std::string MaxFitGapLengthName; + std::string SlidingLinearFitRMSName; + std::string AngleDiffName; + std::string SecondaryPCARatioName; + std::string TertiaryPCARatioName; + std::string VertexDistanceName; + std::string HaloTotalRatioName; + std::string ConcentrationName; + std::string ConicalnessName; + }; + void FillPFPVars(const recob::PFParticle &particle, const recob::PFParticle *primary, const larpandoraobj::PFParticleMetadata *pfpMeta, const art::Ptr t0, caf::SRPFP& srpfp, - bool allowEmpty= false); + const PFOCharLabelsStruct& pfoCharLabels, + bool allowEmpty = false); void FillCNNScores(const recob::PFParticle &particle, const sbn::PFPCNNScore *cnnscore, caf::SRPFP& srpfp, bool allowEmpty = false); + /** + * @brief Fills the results from NuGraph at slice level + * @param sliceHitsMap maps position of hits in collection input to NuGraph (slice only) to the one input to Pandora (all gaus hits) + * @param ngFilterResult NuGraph filter result, for each hit + * @param ngSemanticResult NuGraph semnatic result, for each hit (MIP track, HIP, shower, Michel electron, diffuse activity) + * @param pfpHits Vector of hits associated to the PFParticle + * @param[out] srpfp the destination PFParticle object + * + * Hits with filter value (`ngFilterResult`) lower than `ng_filter_cut` are counted as background. + */ + void FillPFPNuGraph(const std::vector &sliceHitsMap, + const std::vector>> &ngFilterResult, + const std::vector>> &ngSemanticResult, + const std::vector> &pfpHits, + caf::SRPFP& srpfp, + bool allowEmpty= false); + void FillTrackCRTHit(const std::vector> &t0match, const std::vector> &hitmatch, + const std::vector> &hitmatchinfo, bool use_ts0, int64_t CRT_T0_reference_time, // ns, signed double CRT_T1_reference_time, // us + const std::map, sim::AuxDetSimChannel> &crtsimchanmap, caf::SRTrack &srtrack, bool allowEmpty = false); @@ -143,7 +209,8 @@ namespace caf bool allowEmpty = false); void FillTrackCRTSpacePoint(const anab::T0 &t0match, - const art::Ptr &spacepointmatch, + const sbnd::crt::CRTSpacePoint &spacepointmatch, + const sbnd::crt::CRTCluster &cluster, caf::SRTrack &srtrack, bool allowEmpty = false); @@ -166,6 +233,10 @@ namespace caf void FillTrackChi2PID(const std::vector> particleIDs, caf::SRTrack& srtrack, bool allowEmpty = false); + void FillPlaneLikePID(const anab::ParticleID &particle_id, caf::SRTrkLikelihoodPID &srlikepid); + void FillTrackLikePID(const std::vector>& particleIDs, + caf::SRTrack& srtrack, + bool allowEmpty = false); void FillTrackPlaneCalo(const anab::Calorimetry &calo, const std::vector> &hits, @@ -202,6 +273,7 @@ namespace caf bool use_ts0, int64_t CRT_T0_reference_time, // ns, signed double CRT_T1_reference_time, // us + const std::map, sim::AuxDetSimChannel> &crtsimchanmap, caf::SRCRTHit &srhit, bool allowEmpty = false); void FillCRTTrack(const sbn::crt::CRTTrack &track, @@ -210,18 +282,32 @@ namespace caf bool allowEmpty = false); void FillCRTSpacePoint(const sbnd::crt::CRTSpacePoint &spacepoint, + const sbnd::crt::CRTCluster &cluster, caf::SRCRTSpacePoint &srspacepoint, bool allowEmpty = false); void FillSBNDCRTTrack(const sbnd::crt::CRTTrack &track, caf::SRSBNDCRTTrack &srsbndcrttrack, bool allowEmpty = false); + + void FillSBNDCRTVeto(const sbnd::crt::CRTVeto &veto, + const std::vector> &points, + caf::SRSBNDCRTVeto &srsbndcrtveto, + bool allowEmpty = false); - void FillOpFlash(const recob::OpFlash &flash, + void FillICARUSOpFlash(const recob::OpFlash &flash, std::vector const& hits, int cryo, + std::vector RWMTimes, + caf::SROpFlash &srflash, + bool allowEmpty = false); + + void FillSBNDOpFlash(const recob::OpFlash &flash, + std::vector const& hits, + int tpc, caf::SROpFlash &srflash, bool allowEmpty = false); + void FillCRTPMTMatch(const sbn::crt::CRTPMTMatching &match, caf::SRCRTPMTMatch &srmatch, bool allowEmpty = false); @@ -229,6 +315,9 @@ namespace caf void FillTPCPMTBarycenterMatch(const sbn::TPCPMTBarycenterMatch *matchInfo, caf::SRSlice& slice); + void FillCorrectedOpFlashTiming(const std::vector> &slcCorrectedOpFlash, + caf::SRSlice& slice); + void FillCVNScores(const lcvn::Result *cvnResult, caf::SRSlice& slice); @@ -236,6 +325,14 @@ namespace caf caf::SRPFP& srpfp, bool allowEmpty = false); + void FillSBNDFrameShiftInfo(const sbnd::timing::FrameShiftInfo &frame, + caf::SRSBNDFrameShiftInfo &srsbndframe, + bool allowEmpty = false); + + void FillSBNDTimingInfo(const sbnd::timing::TimingInfo &timing, + caf::SRSBNDTimingInfo &srsbndtiming, + bool allowEmpty = false); + template void CopyPropertyIfSet( const std::map& props, const std::string& search, U& value ); } diff --git a/sbncode/CAFMaker/FillTrigger.cxx b/sbncode/CAFMaker/FillTrigger.cxx index 009deccef..f7bd2998b 100644 --- a/sbncode/CAFMaker/FillTrigger.cxx +++ b/sbncode/CAFMaker/FillTrigger.cxx @@ -1,12 +1,13 @@ - +#include +#include #include "sbncode/CAFMaker/FillTrigger.h" namespace caf { void FillTrigger(const sbn::ExtraTriggerInfo& addltrig_info, - const raw::Trigger& trig, - caf::SRTrigger& triggerInfo, - const double time_offset = 0.0) + const raw::Trigger& trig, + caf::SRTrigger& triggerInfo, + const double time_offset = 0.0) { triggerInfo.global_trigger_time = addltrig_info.triggerTimestamp; triggerInfo.beam_gate_time_abs = addltrig_info.beamGateTimestamp; @@ -14,7 +15,6 @@ namespace caf triggerInfo.global_trigger_det_time = trig.TriggerTime() + time_offset; double diff_ts = triggerInfo.global_trigger_det_time - triggerInfo.beam_gate_det_time; triggerInfo.trigger_within_gate = diff_ts; - triggerInfo.prev_global_trigger_time = addltrig_info.previousTriggerTimestamp; triggerInfo.source_type = sbn::bits::value(addltrig_info.sourceType); triggerInfo.trigger_type = sbn::bits::value(addltrig_info.triggerType); @@ -23,16 +23,89 @@ namespace caf triggerInfo.trigger_count = addltrig_info.triggerCount; triggerInfo.gate_count = addltrig_info.gateCount; triggerInfo.gate_delta = addltrig_info.gateCountFromPreviousTrigger; + } - void FillTriggerMC(double absolute_time, caf::SRTrigger& triggerInfo) { + void FillTriggerMC(double absolute_time, caf::SRTrigger& triggerInfo) + { triggerInfo.global_trigger_time = absolute_time; triggerInfo.beam_gate_time_abs = absolute_time; + triggerInfo.trigger_within_gate = 0.; // Set this to 0 since the "MC" trigger is (for now) always at the spill time + } - // Set this to 0 since the "MC" trigger is (for now) always at the spill time - triggerInfo.trigger_within_gate = 0.; + void FillTriggerICARUS(const sbn::ExtraTriggerInfo& addltrig_info, + caf::SRTrigger& triggerInfo) + { + // Choose the cryostat that triggered first (if both are available) + int const cryo = addltrig_info.cryostats[sbn::ExtraTriggerInfo::EastCryostat].beamToTrigger < addltrig_info.cryostats[sbn::ExtraTriggerInfo::WestCryostat].beamToTrigger + ? sbn::ExtraTriggerInfo::EastCryostat + : sbn::ExtraTriggerInfo::WestCryostat; - // TODO: fill others? + sbn::ExtraTriggerInfo::CryostatInfo const& cryoInfo = addltrig_info.cryostats[cryo]; + if (cryoInfo.hasTrigger()) { + triggerInfo.trigger_cryo_source = cryo; + triggerInfo.trigger_logic_bits = cryoInfo.triggerLogicBits; + triggerInfo.gate_to_trigger_time = static_cast(cryoInfo.beamToTrigger) / 1000.0f; // [us] + } } + void FillTriggerSBND(caf::SRSBNDTimingInfo& timingInfo, caf::SRTrigger& triggerInfo){ + + if (timingInfo.hltEtrig != std::numeric_limits::max()) triggerInfo.global_trigger_time = timingInfo.hltEtrig; + if (timingInfo.hltBeamGate != std::numeric_limits::max()) triggerInfo.beam_gate_time_abs = timingInfo.hltBeamGate; + + double diff_ts = std::numeric_limits::max(); + + if ((triggerInfo.global_trigger_time != std::numeric_limits::max()) && (triggerInfo.beam_gate_time_abs != std::numeric_limits::max())){ + + if (triggerInfo.global_trigger_time > triggerInfo.beam_gate_time_abs){ + diff_ts = triggerInfo.global_trigger_time - triggerInfo.beam_gate_time_abs; + } + else{ + diff_ts = -1.0 * (triggerInfo.beam_gate_time_abs - triggerInfo.global_trigger_time); + } + } + triggerInfo.trigger_within_gate = diff_ts; + } + + void FillTriggerEmulation(art::Handle> const& monpulsesFlat, + art::Handle> const& monpulseSizes, + art::Handle const& numPairs, + art::Handle const& passedTrig, + caf::SRTrigger& triggerInfo) { + + triggerInfo.monpulses_flat = *monpulsesFlat; + triggerInfo.monpulse_sizes = *monpulseSizes; + triggerInfo.num_pairs_over_threshold = *numPairs; + triggerInfo.passed_trigger = *passedTrig; + } + + void FillSoftwareTriggerSBND(const sbnd::trigger::pmtSoftwareTrigger& softInfo, caf::SRSoftwareTrigger& caf_softInfo){ + caf_softInfo.npmts = softInfo.nAboveThreshold; + caf_softInfo.flash_peakpe = softInfo.peakPE; + caf_softInfo.flash_peaktime = softInfo.peaktime + softInfo.trig_ts*1e-3; + } + + void FillPTBTriggersSBND(const std::vector& ptb_triggers, caf::SRTrigger& triggerInfo) { + triggerInfo.ptb_hlt_timestamp.clear(); + triggerInfo.ptb_hlt_bit.clear(); + triggerInfo.ptb_llt_timestamp.clear(); + triggerInfo.ptb_llt_bit.clear(); + + // Decode trigger words: each set bit becomes a separate entry with the same timestamp + for(const auto& trig : ptb_triggers) { + // Choose destination vectors based on trigger type + auto& ptb_timestamp = trig.isHLT ? triggerInfo.ptb_hlt_timestamp : triggerInfo.ptb_llt_timestamp; + auto& ptb_bit = trig.isHLT ? triggerInfo.ptb_hlt_bit : triggerInfo.ptb_llt_bit; + + std::bitset<64> const triggerWord { trig.triggerWord }; + // currPTBTimeStamp is already in nanoseconds (uint64_t), use directly + // Loop variable is uint8_t since we know bit values are 0-63 (fits in uint8_t) + for(std::uint8_t bit = 0; bit < triggerWord.size(); ++bit) { + if(!triggerWord[bit]) continue; + ptb_timestamp.push_back(trig.currPTBTimeStamp); + ptb_bit.push_back(bit); + } + } + } } diff --git a/sbncode/CAFMaker/FillTrigger.h b/sbncode/CAFMaker/FillTrigger.h index b83c2e9f0..203fbe57c 100644 --- a/sbncode/CAFMaker/FillTrigger.h +++ b/sbncode/CAFMaker/FillTrigger.h @@ -4,13 +4,15 @@ #include "sbnobj/Common/Trigger/ExtraTriggerInfo.h" #include "sbnobj/Common/Trigger/BeamBits.h" #include "sbnanaobj/StandardRecord/SRTrigger.h" +#include "sbnanaobj/StandardRecord/SRSoftwareTrigger.h" +#include "sbnanaobj/StandardRecord/SRSBNDTimingInfo.h" #include "lardataobj/RawData/TriggerData.h" - -#include +#include "art/Framework/Principal/Handle.h" +#include "sbndaq-artdaq-core/Obj/SBND/pmtSoftwareTrigger.hh" +#include "sbncode/BeamSpillInfoRetriever/POTTools.h" namespace caf { - void FillTrigger(const sbn::ExtraTriggerInfo& addltrig_info, const raw::Trigger& trig_info, caf::SRTrigger& triggerInfo, @@ -18,6 +20,19 @@ namespace caf void FillTriggerMC(double absolute_time, caf::SRTrigger& triggerInfo); + void FillTriggerICARUS(const sbn::ExtraTriggerInfo& addltrig_info, + caf::SRTrigger& triggerInfo); + + void FillTriggerSBND(caf::SRSBNDTimingInfo& timingInfo, caf::SRTrigger& triggerInfo); + + void FillTriggerEmulation(art::Handle> const& monpulsesFlat, + art::Handle> const& monpulseSizes, + art::Handle const& numPairs, + art::Handle const& passedTrig, + caf::SRTrigger& triggerInfo); + void FillSoftwareTriggerSBND(const sbnd::trigger::pmtSoftwareTrigger& softInfo, caf::SRSoftwareTrigger& caf_softInfo); + + void FillPTBTriggersSBND(const std::vector& ptb_triggers, caf::SRTrigger& triggerInfo); } #endif diff --git a/sbncode/CAFMaker/FillTrue.cxx b/sbncode/CAFMaker/FillTrue.cxx index 491c813c0..4d1d794d0 100644 --- a/sbncode/CAFMaker/FillTrue.cxx +++ b/sbncode/CAFMaker/FillTrue.cxx @@ -599,7 +599,7 @@ namespace caf { } // Set the cryostat of the position - for (int icryo = 0; icryo < 2; icryo++) { + for (int icryo = 0; icryo < (int)active_volumes.size(); icryo++) { if (active_volumes[icryo].ContainsPosition(nu.Nu().Position().Vect())) { srneutrino.cryostat = icryo; break; @@ -777,6 +777,9 @@ namespace caf { if (exit_point < 0 && entry_point >= 0) { exit_point = particle.NumberTrajectoryPoints() - 1; } + if(exit_point >= 0 && entry_point >=0 && exit_point == entry_point && exit_point < static_cast(particle.NumberTrajectoryPoints()) - 1){ + exit_point++; // to avoid exactly the same start and end positions when single index is inside the active volumne + } if (exit_point >= 0 && ((unsigned)exit_point) < particle.NumberTrajectoryPoints() - 1) { srparticle.wallout = GetWallCross(active_volumes.at(cryostat_index), particle.Position(exit_point).Vect(), particle.Position(exit_point+1).Vect()); } @@ -1257,38 +1260,47 @@ caf::Wall_t caf::GetWallCross(const geo::BoxBoundedGeo &volume, const TVector3 p TVector3 direction = (p1 - p0) * ( 1. / (p1 - p0).Mag()); std::vector intersections = volume.GetIntersections(p0, direction); - assert(intersections.size() == 2); + /* + * There are either two, or one, or zero intersection points. + * As per larcorealg/Geometry/BoxBoundedGeo.h documentation: + * If the return std::vector is empty the trajectory does not intersect with the box. + * Normally the return value should have one (if the trajectory originates in the box) or two (else) entries. + * If the return value has two entries the first represents the entry point and the second the exit point + */ + + if( intersections.empty() ) return caf::kWallNone; // get the intersection point closer to p0 - int intersection_i = ((intersections[0] - p0).Mag() < (intersections[1] - p0).Mag()) ? 0 : 1; + TVector3 closestIntersection; + double minDistance2 = std::numeric_limits::max(); + + for( TVector3 const & point : intersections ) { + const double d2 = (point - p0).Mag2(); + if( d2 > minDistance2 ) continue; + minDistance2 = d2; + closestIntersection = point; + } double eps = 1e-3; - if (abs(intersections[intersection_i].X() - volume.MinX()) < eps) { - //std::cout << "Left\n"; + if (abs(closestIntersection.X() - volume.MinX()) < eps) { return caf::kWallLeft; } - else if (abs(intersections[intersection_i].X() - volume.MaxX()) < eps) { - //std::cout << "Right\n"; + else if (abs(closestIntersection.X() - volume.MaxX()) < eps) { return caf::kWallRight; } - else if (abs(intersections[intersection_i].Y() - volume.MinY()) < eps) { - //std::cout << "Bottom\n"; + else if (abs(closestIntersection.Y() - volume.MinY()) < eps) { return caf::kWallBottom; } - else if (abs(intersections[intersection_i].Y() - volume.MaxY()) < eps) { - //std::cout << "Top\n"; + else if (abs(closestIntersection.Y() - volume.MaxY()) < eps) { return caf::kWallTop; } - else if (abs(intersections[intersection_i].Z() - volume.MinZ()) < eps) { - //std::cout << "Front\n"; + else if (abs(closestIntersection.Z() - volume.MinZ()) < eps) { return caf::kWallFront; } - else if (abs(intersections[intersection_i].Z() - volume.MaxZ()) < eps) { - //std::cout << "Back\n"; + else if (abs(closestIntersection.Z() - volume.MaxZ()) < eps) { return caf::kWallBack; } else assert(false); - //std::cout << "None\n"; return caf::kWallNone; }//GetWallCross diff --git a/sbncode/CAFMaker/RecoUtils/CMakeLists.txt b/sbncode/CAFMaker/RecoUtils/CMakeLists.txt index a992d7c63..b6abdbdb9 100644 --- a/sbncode/CAFMaker/RecoUtils/CMakeLists.txt +++ b/sbncode/CAFMaker/RecoUtils/CMakeLists.txt @@ -12,3 +12,4 @@ art_make_library( LIBRARY_NAME caf_RecoUtils larsim::MCCheater_ParticleInventoryService_service larcorealg::Geometry ) +install_headers() diff --git a/sbncode/CMakeLists.txt b/sbncode/CMakeLists.txt index 9b7cfc5ed..daed2590a 100644 --- a/sbncode/CMakeLists.txt +++ b/sbncode/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(GeometryTools) add_subdirectory(CosmicID) add_subdirectory(DetSim) add_subdirectory(Cluster3D) +add_subdirectory(HitFinder) # Supera # diff --git a/sbncode/Calibration/CMakeLists.txt b/sbncode/Calibration/CMakeLists.txt index 98059edc5..ae1d41421 100644 --- a/sbncode/Calibration/CMakeLists.txt +++ b/sbncode/Calibration/CMakeLists.txt @@ -1,6 +1,6 @@ cet_build_plugin( TrackCaloSkimmer art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -28,7 +28,7 @@ cet_build_plugin( TrackCaloSkimmer art::module ) cet_build_plugin(TrackCaloSkimmerSelectStoppingTrack art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -47,7 +47,7 @@ cet_build_plugin(TrackCaloSkimmerSelectStoppingTrack art::tool ) cet_build_plugin(TrackCaloSkimmerSelectAnode2CathodeTrack art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -61,7 +61,17 @@ cet_build_plugin(TrackCaloSkimmerSelectAnode2CathodeTrack art::tool ) cet_build_plugin(TrackCaloSkimmerSelectAllTrack art::tool - LIBRARIES + LIBRARIES PRIVATE + sbnobj::Common_Calibration_dict + art::Persistency_Common + art::Utilities canvas::canvas + larcore::Geometry_Geometry_service + cetlib::cetlib cetlib_except::cetlib_except + ROOT::X3d +) + +cet_build_plugin(TrackCaloSkimmerSelectClearCosmicMuon art::tool + LIBRARIES PRIVATE sbnobj::Common_Calibration_dict art::Persistency_Common art::Utilities canvas::canvas @@ -71,7 +81,7 @@ cet_build_plugin(TrackCaloSkimmerSelectAllTrack art::tool ) cet_build_plugin ( DataSelect art::module - LIBRARIES + LIBRARIES PRIVATE ifdh_art::IFDH_service art::Framework_Core art::Framework_Principal diff --git a/sbncode/Calibration/TrackCaloSkimmer.h b/sbncode/Calibration/TrackCaloSkimmer.h index 4881f9f2f..497959518 100644 --- a/sbncode/Calibration/TrackCaloSkimmer.h +++ b/sbncode/Calibration/TrackCaloSkimmer.h @@ -61,6 +61,11 @@ #include "larsim/MCCheater/ParticleInventoryService.h" #include "sbnobj/Common/Calibration/TrackCaloSkimmerObj.h" +#include "sbnobj/Common/CRT/CRTHitT0TaggingInfo.hh" +#include "sbnobj/Common/CRT/CRTHitT0TaggingTruthInfo.hh" +#include "sbnobj/SBND/CRT/CRTTrack.hh" +#include "sbnobj/SBND/CRT/CRTSpacePoint.hh" + #include "ITCSSelectionTool.h" namespace sbn { @@ -90,6 +95,19 @@ class sbn::TrackCaloSkimmer : public art::EDAnalyzer { } private: + + struct T0TimingInfo { + double t0Pandora; + double t0CRTTrack; + double t0CRTHit; + double t0CRTSpacePoint; + bool hasT0Pandora; + bool hasT0CRTTrack; + bool hasT0CRTHit; + bool hasT0CRTSpacePoint; + double crtMatchingScore; + }; + // Internal data struct struct GlobalTrackInfo { geo::Point_t start; @@ -117,13 +135,14 @@ class sbn::TrackCaloSkimmer : public art::EDAnalyzer { // Fill vars void FillTrack(const recob::Track &track, - const recob::PFParticle &pfp, float t0, float t0CRT, + const recob::PFParticle &pfp, const T0TimingInfo &t0Info, const std::vector> &hits, const std::vector &thms, const std::vector> &sps, const std::vector> &calo, const std::map> &rawdigits, const std::vector &tracks, + const geo::GeometryCore *geo, const geo::WireReadoutGeom *wireReadout, const detinfo::DetectorClocksData &clock_data, const cheat::BackTrackerService *bt_serv, @@ -157,19 +176,29 @@ class sbn::TrackCaloSkimmer : public art::EDAnalyzer { unsigned hkey, const recob::TrackHitMeta &thm, const recob::Track &trk, + const T0TimingInfo &t0Info, const art::Ptr &sp, const std::vector> &calo, + const geo::GeometryCore *geo, const geo::WireReadoutGeom *wireReadout, const detinfo::DetectorClocksData &dclock, - const cheat::BackTrackerService *bt_serv); + const cheat::BackTrackerService *bt_serv, + const detinfo::DetectorPropertiesData &dprop, + const float &xshift); + void FillTrackCRTHitInfo(const std::vector> &tag); + // helpers void DoTailFit(); + bool PointIsContained(const std::vector &vols, geo::Point_t p); // config // tags art::InputTag fPFPproducer; - std::vector fT0producers; + art::InputTag fPFPT0producer; + art::InputTag fCRTTrackT0producer; + art::InputTag fCRTHitT0producer; + art::InputTag fCRTSpacePointT0producer; art::InputTag fCALOproducer; art::InputTag fTRKproducer; art::InputTag fTRKHMproducer; @@ -187,7 +216,15 @@ class sbn::TrackCaloSkimmer : public art::EDAnalyzer { bool fFillTrackEndHits; float fTrackEndHitWireBox; float fTrackEndHitTimeBox; - + bool fIncludeCRTHitTagging; + bool fIncludeTopCRT; + bool fIncludeSideCRT; + double fTopCRTDistanceCutStopping; + double fTopCRTDistanceCutPassing; + double fSideCRTDistanceCutStopping; + double fSideCRTDistanceCutPassing; + bool fAllowShowerLikePFPs; + // tools std::vector> fSelectionTools; diff --git a/sbncode/Calibration/TrackCaloSkimmerSelectClearCosmicMuon_tool.cc b/sbncode/Calibration/TrackCaloSkimmerSelectClearCosmicMuon_tool.cc new file mode 100644 index 000000000..9346e953f --- /dev/null +++ b/sbncode/Calibration/TrackCaloSkimmerSelectClearCosmicMuon_tool.cc @@ -0,0 +1,30 @@ +// Framework Includes +#include "art/Utilities/ToolMacros.h" +#include "fhiclcpp/ParameterSet.h" + +#include "ITCSSelectionTool.h" + +namespace sbn { + +class TrackCaloSkimmerSelectClearCosmicMuon: public ITCSSelectionTool { +public: + + TrackCaloSkimmerSelectClearCosmicMuon(const fhicl::ParameterSet &p); + ~TrackCaloSkimmerSelectClearCosmicMuon() {} + + bool Select(const TrackInfo &t) override; + +private: +}; + +TrackCaloSkimmerSelectClearCosmicMuon::TrackCaloSkimmerSelectClearCosmicMuon(const fhicl::ParameterSet &p): + ITCSSelectionTool(p) +{} + +bool TrackCaloSkimmerSelectClearCosmicMuon::Select(const TrackInfo &t) { + return t.clear_cosmic_muon; +} + +DEFINE_ART_CLASS_TOOL(TrackCaloSkimmerSelectClearCosmicMuon) + +} // end namespace sbn diff --git a/sbncode/Calibration/TrackCaloSkimmer_module.cc b/sbncode/Calibration/TrackCaloSkimmer_module.cc index 152907901..00a02b7fc 100644 --- a/sbncode/Calibration/TrackCaloSkimmer_module.cc +++ b/sbncode/Calibration/TrackCaloSkimmer_module.cc @@ -11,7 +11,6 @@ //////////////////////////////////////////////////////////////////////// #include "TrackCaloSkimmer.h" -#include "sbnobj/SBND/CRT/CRTTrack.hh" #include "art/Utilities/make_tool.h" @@ -68,7 +67,11 @@ sbn::TrackCaloSkimmer::TrackCaloSkimmer(fhicl::ParameterSet const& p) { // Grab config fPFPproducer = p.get< art::InputTag > ("PFPproducer","pandoraGausCryo0"); - fT0producers = p.get< std::vector > ("T0producers", {"pandoraGausCryo0"} ); + fPFPT0producer = p.get< art::InputTag > ("PFPT0producer", "pandoraGausCryo0"); + fCRTTrackT0producer = p.get< art::InputTag >("CRTTrackT0producer", "crttrackmatching"); + fCRTSpacePointT0producer = p.get< art::InputTag >("CRTSpacePointT0producer", "crtspacepointmatching"); + fCRTHitT0producer = p.get< art::InputTag >("CRTHitT0producer", "CRTT0Tagging"); + fCALOproducer = p.get< art::InputTag > ("CALOproducer"); fTRKproducer = p.get< art::InputTag > ("TRKproducer" ); fTRKHMproducer= p.get< art::InputTag > ("TRKHMproducer", ""); @@ -82,6 +85,15 @@ sbn::TrackCaloSkimmer::TrackCaloSkimmer(fhicl::ParameterSet const& p) fHitRawDigitsTickCollectWidth = p.get("HitRawDigitsTickCollectWidth", 50.); fHitRawDigitsWireCollectWidth = p.get("HitRawDigitsWireCollectWidth", 5); fTailFitResidualRange = p.get("TailFitResidualRange", 5.); + fIncludeCRTHitTagging = p.get("IncludeCRTHitTagging", false); + fIncludeTopCRT = p.get("IncludeTopCRT", false); + fIncludeSideCRT = p.get("IncludeSideCRT", false); + fTopCRTDistanceCutStopping = p.get("TopCRTDistanceCut_stopping", 100.); + fTopCRTDistanceCutPassing = p.get("TopCRTDistanceCut_throughgoing", 100.); + fSideCRTDistanceCutStopping = p.get("SideCRTDistanceCut_stopping", 100.); + fSideCRTDistanceCutPassing = p.get("SideCRTDistanceCut_throughgoing", 100.); + fAllowShowerLikePFPs = p.get("AllowShowerLikePFPs", false); + if (fTailFitResidualRange > 10.) { std::cout << "sbn::TrackCaloSkimmer: Bad tail fit residual range config :(" << fTailFitResidualRange << "). Fits will not be meaningful.\n"; } @@ -128,11 +140,9 @@ void sbn::TrackCaloSkimmer::analyze(art::Event const& e) fMeta.subrun = sub; fMeta.run = run; fMeta.time = e.time().value(); - // Services const geo::GeometryCore *geometry = lar::providerFrom(); - const geo::WireReadoutGeom *wireReadout = - &art::ServiceHandle()->Get(); + const geo::WireReadoutGeom *wireReadout = &art::ServiceHandle()->Get(); auto const clock_data = art::ServiceHandle()->DataFor(e); auto const dprop = art::ServiceHandle()->DataFor(e, clock_data); @@ -212,24 +222,22 @@ void sbn::TrackCaloSkimmer::analyze(art::Event const& e) } // PFP-associated data - std::vector> fmT0; - for (unsigned i_label = 0; i_label < fT0producers.size(); i_label++) { - if (fT0producers[i_label] != "crttrackmatching") - fmT0.emplace_back(PFParticleList, e, fT0producers[i_label]); - } + art::FindManyP fmT0PFP(PFParticleList, e, fPFPT0producer); art::FindManyP PFParticleSPs(PFParticleList, e, fPFPproducer); // Now we don't need to guard access to further data. If this is an empty event it should be caught by PFP's or Hit's art::ValidHandle> tracks = e.getValidHandle>(fTRKproducer); - std::vector> fmT02; - float crtLabel = std::numeric_limits::signaling_NaN(); - for (unsigned i_label = 0; i_label < fT0producers.size(); i_label++) { - if (fT0producers[i_label] == "crttrackmatching"){ - crtLabel = i_label; - fmT02.emplace_back(tracks, e, fT0producers[i_label]); - } - } + // Get CRT T0s + // + // Tracks (SBND style) + art::FindManyP fmT0CRTTrack(tracks, e, fCRTTrackT0producer); + // SpacePoints (SBND style) + art::FindManyP fmT0CRTSpacePoint(tracks, e, fCRTSpacePointT0producer); + + // Hits (ICARUS style) + art::FindManyP fmT0CRTHit(tracks, e, fCRTHitT0producer); + art::FindManyP fmCRTHitT0TaggingInfo(tracks, e, fCRTHitT0producer); // Track - associated data art::FindManyP fmTracks(PFParticleList, e, fTRKproducer); @@ -308,6 +316,9 @@ void sbn::TrackCaloSkimmer::analyze(art::Event const& e) for (art::Ptr p_pfp: PFParticleList) { const recob::PFParticle &pfp = *p_pfp; + if(p_pfp->PdgCode() == 11 && !fAllowShowerLikePFPs) + continue; + const std::vector> thisTrack = fmTracks.at(p_pfp.key()); if (thisTrack.size() != 1) continue; @@ -339,40 +350,94 @@ void sbn::TrackCaloSkimmer::analyze(art::Event const& e) } } + // Collect T0s + bool hasT0 = false, hasPFPT0 = false, hasCRTTrackT0 = false, hasCRTHitT0 = false, hasCRTSpacePointT0 = false; int whicht0 = -1; - float t0 = std::numeric_limits::signaling_NaN(); - for (unsigned i_t0 = 0; i_t0 < fmT0.size(); i_t0++) { - if (fmT0[i_t0].isValid() && fmT0[i_t0].at(p_pfp.key()).size()) { - t0 = fmT0[i_t0].at(p_pfp.key()).at(0)->Time(); - whicht0 = i_t0; + float crtMatchingScore = std::numeric_limits::signaling_NaN(); - if (fVerbose) std::cout << "Track: " << trkPtr->ID() << " Has T0 (" << fT0producers[i_t0] << ")\n"; + double t0PFP = std::numeric_limits::signaling_NaN(); + if (fmT0PFP.isValid() && fmT0PFP.at(p_pfp.key()).size()) { + t0PFP = fmT0PFP.at(p_pfp.key()).at(0)->Time(); + hasPFPT0 = true; + if (fVerbose) std::cout << "Track: " << trkPtr->ID() << " Has PFPT0 (" << fPFPT0producer << ")\n"; + } - break; - } + double t0CRTTrack = std::numeric_limits::signaling_NaN(); + double t0CRTTrackScore = std::numeric_limits::signaling_NaN(); + if (fmT0CRTTrack.isValid() && fmT0CRTTrack.at(trkPtr.key()).size()) { + t0CRTTrack = fmT0CRTTrack.data(trkPtr.key()).at(0)->Time(); + t0CRTTrackScore = fmT0CRTTrack.data(trkPtr.key()).at(0)->TriggerConfidence(); + hasCRTTrackT0 = true; } - float t0CRT = std::numeric_limits::signaling_NaN(); - for (unsigned i_t0 = 0; i_t0 < fmT02.size(); i_t0++) { - if (fmT02[i_t0].isValid()) { - try { - t0CRT = fmT02[i_t0].data(trkPtr.key()).ref().Time(); - whicht0 = i_t0; + double t0CRTSpacePoint = std::numeric_limits::signaling_NaN(); + double t0CRTSpacePointScore = std::numeric_limits::signaling_NaN(); + if (fmT0CRTSpacePoint.isValid() && fmT0CRTSpacePoint.at(trkPtr.key()).size()) { + t0CRTSpacePoint = fmT0CRTSpacePoint.data(trkPtr.key()).at(0)->Time(); + t0CRTSpacePointScore = fmT0CRTSpacePoint.data(trkPtr.key()).at(0)->TriggerConfidence(); + hasCRTSpacePointT0 = true; + } - if (fVerbose) std::cout << "Track: " << trkPtr->ID() << " Has CRTT0 (" << fT0producers[crtLabel] << ")\n"; - } - catch(...) {} - break; + double t0CRTHit = std::numeric_limits::signaling_NaN(); + double t0CRTHitScore = std::numeric_limits::signaling_NaN(); + if (fIncludeCRTHitTagging && fmT0CRTHit.isValid() && fmT0CRTHit.at(trkPtr.key()).size()) { + const sbn::crt::CRTHitT0TaggingInfo &tag = *fmCRTHitT0TaggingInfo.at(trkPtr.key()).at(0); + double time = fmT0CRTHit.at(trkPtr.key()).at(0)->Time(); + + // Whether to select the wall of the hit + bool crtHitSysRejected = (tag.Sys == 0 && !fIncludeTopCRT) || (tag.Sys==1 && !fIncludeSideCRT); + + // Whether to cut on the distance (depends on whether track is stopping) + geo::Point_t end {trkPtr->Start().X(), trkPtr->Start().Y(), trkPtr->Start().Z()}; + if (!hasPFPT0 && trkHits.size()) { // correct X position if we need to + int driftDir = geometry->TPC(trkHits.at(0)->WireID()).DriftDir().X(); + double driftv = dprop.DriftVelocity(); + end.SetX(end.X() + time*driftDir*driftv*1e-3); } - } + bool trackIsStopping = PointIsContained(AVs, end); - if (fRequireT0 && whicht0 < 0) { - continue; + bool crtHitDistanceRejected = trackIsStopping ? + ((tag.Sys == 0) ? (tag.Distance > fTopCRTDistanceCutStopping) : (tag.Distance > fSideCRTDistanceCutStopping)) : + ((tag.Sys == 0) ? (tag.Distance > fTopCRTDistanceCutPassing) : (tag.Distance > fSideCRTDistanceCutPassing)); + + if (!crtHitSysRejected && !crtHitDistanceRejected) { + t0CRTHit = time; + t0CRTHitScore = tag.Distance; + hasCRTHitT0 = true; + } } - if (fVerbose) std::cout << "Processing new track! ID: " << trkPtr->ID() << " time: " << t0 << std::endl; - if (fVerbose) std::cout << "Processing new track! ID: " << trkPtr->ID() << " timeCRT: " << t0CRT << std::endl; + hasT0 = hasPFPT0 || hasCRTTrackT0 || hasCRTHitT0 || hasCRTSpacePointT0; + // "whicht0" should reflect the T0 used for the reconstruction of the drift coordinate. + if(!hasT0) whicht0 = -1; + // In this way, if a track is T0 tagged from PFP and CRT tagged, which T0 reflects the PFP Tag. + else if (hasPFPT0) whicht0 = 0; + else if (hasCRTTrackT0) + { + whicht0 = 1; + crtMatchingScore = t0CRTTrackScore; + } + else if (hasCRTHitT0) + { + whicht0 = 2; + crtMatchingScore = t0CRTHitScore; + } + else if (hasCRTSpacePointT0) + { + whicht0 = 3; + crtMatchingScore = t0CRTSpacePointScore; + } + + T0TimingInfo thisTrackTimingInfo = {t0PFP, t0CRTTrack, t0CRTHit, t0CRTSpacePoint, hasPFPT0, hasCRTTrackT0, hasCRTHitT0, hasCRTSpacePointT0, crtMatchingScore}; + + if (fRequireT0 && !hasT0) continue; + + if (fVerbose) std::cout << "Processing new track! ID: " << trkPtr->ID() << "\n" + << "\tPandora time: " << t0PFP << "\n" + << "\tCRTTrack (SBND) time: " << t0CRTTrack << "\n" + << "\tCRTHit (ICARUS) time: " << t0CRTHit << "\n" + << "\tCRTSpacePoint (SBND) time: " << t0CRTSpacePoint << std::endl; // Reset the track object *fTrack = sbn::TrackInfo(); @@ -380,31 +445,27 @@ void sbn::TrackCaloSkimmer::analyze(art::Event const& e) // Reset other persistent info fSnippetCount.clear(); fWiresToSave.clear(); - // Fill the track! - FillTrack(*trkPtr, pfp, t0, t0CRT, trkHits, trkHitMetas, trkHitSPs, calo, rawdigits, track_infos, wireReadout, clock_data, bt, det, dprop); + FillTrack(*trkPtr, pfp, thisTrackTimingInfo, trkHits, trkHitMetas, trkHitSPs, calo, rawdigits, track_infos, geometry, wireReadout, clock_data, bt, det, dprop); fTrack->whicht0 = whicht0; - FillTrackDaughterRays(*trkPtr, pfp, PFParticleList, PFParticleSPs); if (fFillTrackEndHits) FillTrackEndHits(geometry, wireReadout, dprop, *trkPtr, allHits, allHitSPs); + // Fill CRT info if we can + if (fmCRTHitT0TaggingInfo.isValid()) FillTrackCRTHitInfo(fmCRTHitT0TaggingInfo.at(trkPtr.key())); + // Fill the truth information if configured if (simchannels.size()) FillTrackTruth(clock_data, trkHits, mcparticles, AVs, TPCVols, id_to_ide_map, id_to_truehit_map, dprop, geometry, wireReadout); // Save? bool select = false; if (!fSelectionTools.size()) select = true; - // Take the OR of each selection tool int i_select = 0; for (const std::unique_ptr &t: fSelectionTools) { if (t->DoSelect(*fTrack)) { select = true; - if (!isnan(fTrack->t0CRT) && isnan(fTrack->t0)) - i_select = i_select + 10; - if (!isnan(fTrack->t0CRT) && !isnan(fTrack->t0)) - i_select = i_select + 100; fTrack->selected = i_select; fTrack->nprescale = t->GetPrescale(); break; @@ -800,12 +861,10 @@ sbn::TrueParticle TrueParticleInfo(const simb::MCParticle &particle, TVector3 loc_mdx_v = h_p - direction * (planeGeo.WirePitch() / 2.); TVector3 loc_pdx_v = h_p + direction * (planeGeo.WirePitch() / 2.); - // Convert types for helper functions geo::Point_t loc_mdx(loc_mdx_v.X(), loc_mdx_v.Y(), loc_mdx_v.Z()); geo::Point_t loc_pdx(loc_pdx_v.X(), loc_pdx_v.Y(), loc_pdx_v.Z()); geo::Point_t h_p_point(h_p.X(), h_p.Y(), h_p.Z()); - auto const driftdir = geo->TPC(plane).DriftDir(); loc_mdx = TrajectoryToWirePosition(loc_mdx, driftdir); loc_pdx = TrajectoryToWirePosition(loc_pdx, driftdir); @@ -1062,14 +1121,35 @@ void sbn::TrackCaloSkimmer::FillTrackDaughterRays(const recob::Track &trk, } +bool sbn::TrackCaloSkimmer::PointIsContained(const std::vector &vols, geo::Point_t p) { + for (auto const &v: vols) { + if (v.ContainsPosition(p)) return true; + } + return false; +} + +void sbn::TrackCaloSkimmer::FillTrackCRTHitInfo(const std::vector> &tag) { + fTrack->PCAdir.x = std::numeric_limits::signaling_NaN(); + fTrack->PCAdir.y = std::numeric_limits::signaling_NaN(); + fTrack->PCAdir.z = std::numeric_limits::signaling_NaN(); + + if (!tag.size()) return; + + const sbn::crt::CRTHitT0TaggingInfo &t = *tag.at(0); + fTrack->PCAdir.x = t.PCAEigenVector.X(); + fTrack->PCAdir.y = t.PCAEigenVector.Y(); + fTrack->PCAdir.z = t.PCAEigenVector.Z(); +} + void sbn::TrackCaloSkimmer::FillTrack(const recob::Track &track, - const recob::PFParticle &pfp, float t0, float t0CRT, + const recob::PFParticle &pfp, const T0TimingInfo &t0Info, const std::vector> &hits, const std::vector &thms, const std::vector> &sps, const std::vector> &calo, const std::map> &rawdigits, const std::vector &tracks, + const geo::GeometryCore *geo, const geo::WireReadoutGeom *wireReadout, const detinfo::DetectorClocksData &clock_data, const cheat::BackTrackerService *bt_serv, @@ -1078,8 +1158,11 @@ void sbn::TrackCaloSkimmer::FillTrack(const recob::Track &track, // Fill top level stuff fTrack->meta = fMeta; - fTrack->t0 = t0; - fTrack->t0CRT = t0CRT; + fTrack->t0PFP = t0Info.t0Pandora; + fTrack->t0CRTTrack = t0Info.t0CRTTrack; + fTrack->t0CRTHit = t0Info.t0CRTHit; + fTrack->t0CRTSpacePoint = t0Info.t0CRTSpacePoint; + fTrack->crtMatchingScore = t0Info.crtMatchingScore; fTrack->id = track.ID(); fTrack->clear_cosmic_muon = pfp.Parent() == recob::PFParticle::kPFParticlePrimary; @@ -1090,19 +1173,41 @@ void sbn::TrackCaloSkimmer::FillTrack(const recob::Track &track, fTrack->end.z = track.End().Z(); fTrack->dir.x = track.StartDirection().X(); fTrack->dir.y = track.StartDirection().Y(); - fTrack->dir.z = track.StartDirection().Z(); - - //If track is only CRTt0 tagged, undo the assumed trigger correction - if (!isnan(fTrack->t0)) { - fTrack->start.x = track.Start().X(); - fTrack->end.x = track.End().X(); - } - else if (isnan(fTrack->t0) && !isnan(fTrack->t0CRT)) { - const double driftv(dprop.DriftVelocity(dprop.Efield(), dprop.Temperature())); + fTrack->dir.z = track.StartDirection().Z(); - fTrack->start.x = track.Start().X() + driftv*t0CRT*1e-3; - fTrack->end.x = track.End().X() + driftv*t0CRT*1e-3; - } + if(t0Info.hasT0Pandora) + { + fTrack->start.x = track.Start().X(); + fTrack->end.x = track.End().X(); + } + else + { + // If CRT T0 tagged we can shift the x positions appropriately + int driftDir = geo->TPC(hits[0]->WireID()).DriftDir().X(); + const double driftv(dprop.DriftVelocity(dprop.Efield(), dprop.Temperature())); + + if(t0Info.hasT0CRTTrack) + { + const double xshift = driftDir*driftv*t0Info.t0CRTTrack*1e-3; + fTrack->start.x = track.Start().X() + xshift; + fTrack->end.x = track.End().X() + xshift; + fTrack->xShiftCRT = xshift; + } + else if(t0Info.hasT0CRTHit) + { + const double xshift = driftDir*driftv*t0Info.t0CRTHit*1e-3; + fTrack->start.x = track.Start().X() + xshift; + fTrack->end.x = track.End().X() + xshift; + fTrack->xShiftCRT = xshift; + } + else if(t0Info.hasT0CRTSpacePoint) + { + const double xshift = driftDir*driftv*t0Info.t0CRTSpacePoint*1e-3; + fTrack->start.x = track.Start().X() + xshift; + fTrack->end.x = track.End().X() + xshift; + fTrack->xShiftCRT = xshift; + } + } if (hits.size() > 0) { fTrack->cryostat = hits[0]->WireID().Cryostat; @@ -1110,7 +1215,7 @@ void sbn::TrackCaloSkimmer::FillTrack(const recob::Track &track, // Fill each hit for (unsigned i_hit = 0; i_hit < hits.size(); i_hit++) { - sbn::TrackHitInfo hinfo = MakeHit(*hits[i_hit], hits[i_hit].key(), *thms[i_hit], track, sps[i_hit], calo, wireReadout, clock_data, bt_serv); + sbn::TrackHitInfo hinfo = MakeHit(*hits[i_hit], hits[i_hit].key(), *thms[i_hit], track, t0Info, sps[i_hit], calo, geo, wireReadout, clock_data, bt_serv, dprop, fTrack->xShiftCRT); if (hinfo.h.plane == 0) { fTrack->hits0.push_back(hinfo); } @@ -1270,11 +1375,15 @@ sbn::TrackHitInfo sbn::TrackCaloSkimmer::MakeHit(const recob::Hit &hit, unsigned hkey, const recob::TrackHitMeta &thm, const recob::Track &trk, + const T0TimingInfo &t0Info, const art::Ptr &sp, const std::vector> &calo, + const geo::GeometryCore *geo, const geo::WireReadoutGeom *wireReadout, const detinfo::DetectorClocksData &dclock, - const cheat::BackTrackerService *bt_serv) { + const cheat::BackTrackerService *bt_serv, + const detinfo::DetectorPropertiesData &dprop, + const float &xshift) { // TrackHitInfo to save sbn::TrackHitInfo hinfo; @@ -1358,7 +1467,10 @@ sbn::TrackHitInfo sbn::TrackCaloSkimmer::MakeHit(const recob::Hit &hit, // Save trajectory information if we can if (!badhit) { geo::Point_t loc = trk.LocationAtPoint(thm.Index()); + + // The tp X coordinate is reconstructed only if it does not have a PandoraT0. hinfo.tp.x = loc.X(); + if((t0Info.hasT0CRTTrack || t0Info.hasT0CRTHit || t0Info.hasT0CRTSpacePoint) && !t0Info.hasT0Pandora && !isnan(hinfo.tp.x)) hinfo.tp.x = loc.X() + xshift; hinfo.tp.y = loc.Y(); hinfo.tp.z = loc.Z(); @@ -1389,15 +1501,15 @@ sbn::TrackHitInfo sbn::TrackCaloSkimmer::MakeHit(const recob::Hit &hit, // Save SpacePoint information if (sp) { hinfo.h.sp.x = sp->position().x(); + // If the track has a Pandora T0, do not displace, track is already reconstructed at the correct position + if((t0Info.hasT0CRTTrack || t0Info.hasT0CRTHit || t0Info.hasT0CRTSpacePoint) && !t0Info.hasT0Pandora) hinfo.h.sp.x = sp->position().x() + xshift; hinfo.h.sp.y = sp->position().y(); hinfo.h.sp.z = sp->position().z(); - hinfo.h.hasSP = true; } else { hinfo.h.hasSP = false; } - return hinfo; } diff --git a/sbncode/Calibration/fcl/icarus/icarus_trackcalo_skimmer.fcl b/sbncode/Calibration/fcl/icarus/icarus_trackcalo_skimmer.fcl index 4953e8823..6f6c5fe65 100644 --- a/sbncode/Calibration/fcl/icarus/icarus_trackcalo_skimmer.fcl +++ b/sbncode/Calibration/fcl/icarus/icarus_trackcalo_skimmer.fcl @@ -26,7 +26,7 @@ caloskim_cryoe: { module_type: TrackCaloSkimmer CALOproducer: "caloskimCalorimetryCryoE" TRKproducer: "pandoraTrackGausCryoE" - T0producers: ["pandoraGausCryoE"] + PFPT0producer: "pandoraGausCryoE" PFPproducer: "pandoraGausCryoE" G4producer: "" SimChannelproducer: "" @@ -43,7 +43,7 @@ caloskim_cryow: { module_type: TrackCaloSkimmer CALOproducer: "caloskimCalorimetryCryoW" TRKproducer: "pandoraTrackGausCryoW" - T0producers: ["pandoraGausCryoW"] + PFPT0producer: "pandoraGausCryoW" PFPproducer: "pandoraGausCryoW" G4producer: "" SimChannelproducer: "" @@ -114,6 +114,32 @@ selectall: { RequireT0: false } +# Include also crthittagged tracks +caloskim_crthittagging_cryoe: @local::caloskim_cryoe +caloskim_crthittagging_cryoe.IncludeCRTHitTagging: true +caloskim_crthittagging_cryoe.IncludeTopCRT: true +caloskim_crthittagging_cryoe.IncludeSideCRT: false +caloskim_crthittagging_cryoe.TopCRTDistanceCut_stopping: 100. +caloskim_crthittagging_cryoe.TopCRTDistanceCut_throughgoing: 100. +caloskim_crthittagging_cryoe.SideCRTDistanceCut_stopping: 100. +caloskim_crthittagging_cryoe.SideCRTDistanceCut_throughgoing: 100. + + +caloskim_crthittagging_cryow: @local::caloskim_cryow +caloskim_crthittagging_cryow.IncludeCRTHitTagging: true +caloskim_crthittagging_cryow.IncludeTopCRT: true +caloskim_crthittagging_cryow.IncludeSideCRT: false +caloskim_crthittagging_cryow.TopCRTDistanceCut_stopping: 60. +caloskim_crthittagging_cryow.TopCRTDistanceCut_throughgoing: 50. +caloskim_crthittagging_cryow.SideCRTDistanceCut_stopping: 100. +caloskim_crthittagging_cryow.SideCRTDistanceCut_throughgoing: 100. + +crthittagged_stopping_selection: @local::stopping_selection +crthittagged_stopping_selection.AllowT0: [true, false, true] + +crthittagged_throughgoing_selection: @local::throughgoing_selection +crthittagged_throughgoing_selection.AllowT0: [true, false, true] + # Require T0 tag, don't save raw waveforms, caloskim_cryow_nodigits_goldentracks: @local::caloskim_cryow_nodigits caloskim_cryow_nodigits_goldentracks.SelectionTools: [@local::stopping_selection, @local::a2c_selection, @local::throughgoing_selection] @@ -142,4 +168,10 @@ caloskim_cryoe_nodigits_goldentracks_not0: @local::caloskim_cryoe_nodigits caloskim_cryoe_nodigits_goldentracks_not0.RequireT0: false caloskim_cryoe_nodigits_goldentracks_not0.SelectionTools: [@local::stopping_selection, @local::a2c_selection, @local::throughgoing_selection, @local::selectall] +# Require T0 tag, allow Top CRT Hit tagging, do save raw waveforms +caloskim_cryow_crthittagged_goldentracks: @local::caloskim_crthittagging_cryow +caloskim_cryow_crthittagged_goldentracks.SelectionTools: [@local::crthittagged_stopping_selection, @local::a2c_selection, @local::crthittagged_throughgoing_selection] +caloskim_cryoe_crthittagged_goldentracks: @local::caloskim_crthittagging_cryoe +caloskim_cryoe_crthittagged_goldentracks.SelectionTools: [@local::crthittagged_stopping_selection, @local::a2c_selection, @local::crthittagged_throughgoing_selection] + END_PROLOG diff --git a/sbncode/Calibration/fcl/sbnd/sbnd_trackcalo_skimmer.fcl b/sbncode/Calibration/fcl/sbnd/sbnd_trackcalo_skimmer.fcl index ea73bdd9b..b510a231f 100644 --- a/sbncode/Calibration/fcl/sbnd/sbnd_trackcalo_skimmer.fcl +++ b/sbncode/Calibration/fcl/sbnd/sbnd_trackcalo_skimmer.fcl @@ -27,7 +27,7 @@ caloskim: { module_type: TrackCaloSkimmer CALOproducer: "caloskimCalorimetry" TRKproducer: "pandoraTrack" - T0producers: ["pandora", "crttrackmatching"] + PFPT0producer: "pandora" PFPproducer: "pandora" G4producer: "largeant" SimChannelproducer: "simtpc2d:simpleSC" @@ -83,6 +83,6 @@ a2c_selection: { } caloskim_nodigits_goldentracks: @local::caloskim_nodigits -caloskim_nodigits_goldentracks.SelectionTools: [@local::stopping_selection, @local::a2c_selection, @local::throughgoing_selection] +caloskim_nodigits_goldentracks.SelectionTools: [ @local::stopping_selection, @local::a2c_selection, @local::throughgoing_selection ] END_PROLOG diff --git a/sbncode/Cluster3D/CMakeLists.txt b/sbncode/Cluster3D/CMakeLists.txt index dad21f80b..c7a8bfcaf 100644 --- a/sbncode/Cluster3D/CMakeLists.txt +++ b/sbncode/Cluster3D/CMakeLists.txt @@ -56,8 +56,8 @@ set( MODULE_LIBRARIES art_root_io::TFileService_service messagefacility::MF_MessageLogger ) -cet_build_plugin(Cluster3DSBN art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(SnippetHit3DBuilderSBN art::tool LIBRARIES ${TOOL_LIBRARIES} SOURCE SnippetHit3DBuilderSBN_tool.cc) +cet_build_plugin(Cluster3DSBN art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(SnippetHit3DBuilderSBN art::tool LIBRARIES PRIVATE ${TOOL_LIBRARIES}) set_property(SOURCE SnippetHit3DBuilderSBN_tool.cc APPEND PROPERTY COMPILE_DEFINITIONS EIGEN_FFTW_DEFAULT) install_headers() diff --git a/sbncode/CosmicID/CMakeLists.txt b/sbncode/CosmicID/CMakeLists.txt index 527721870..23b551470 100644 --- a/sbncode/CosmicID/CMakeLists.txt +++ b/sbncode/CosmicID/CMakeLists.txt @@ -1,5 +1,5 @@ cet_build_plugin( CRUMBS art::module - LIBRARIES + LIBRARIES PRIVATE art_root_io::TFileService_service fhiclcpp::fhiclcpp art::Persistency_Provenance canvas::canvas diff --git a/sbncode/DetSim/AdjustSimForTrigger_module.cc b/sbncode/DetSim/AdjustSimForTrigger_module.cc index 14c7cabe2..5f6c5eee9 100644 --- a/sbncode/DetSim/AdjustSimForTrigger_module.cc +++ b/sbncode/DetSim/AdjustSimForTrigger_module.cc @@ -22,6 +22,7 @@ #include "lardataobj/Simulation/AuxDetSimChannel.h" #include "lardataobj/Simulation/BeamGateInfo.h" #include "lardataobj/Simulation/SimEnergyDeposit.h" +#include "lardataobj/Simulation/SimEnergyDepositLite.h" #include "lardataobj/Simulation/SimPhotons.h" #include @@ -49,11 +50,13 @@ class AdjustSimForTrigger : public art::EDProducer { art::InputTag fInitAuxDetSimChannelLabel; art::InputTag fInitBeamGateInfoLabel; art::InputTag fInitSimEnergyDepositLabel; + art::InputTag fInitSimEnergyDepositLiteLabel; art::InputTag fInitSimPhotonsLabel; art::InputTag fInitWaveformLabel; bool fShiftAuxDetIDEs; bool fShiftBeamGateInfo; bool fShiftSimEnergyDeposits; + bool fShiftSimEnergyDepositLites; bool fShiftSimPhotons; bool fShiftWaveforms; double fAdditionalOffset; @@ -66,29 +69,33 @@ AdjustSimForTrigger::AdjustSimForTrigger(fhicl::ParameterSet const& p) , fInitAuxDetSimChannelLabel(p.get("InitAuxDetSimChannelLabel", "undefined")) , fInitBeamGateInfoLabel{p.get("InitBeamGateInfoLabel", "undefined")} , fInitSimEnergyDepositLabel{p.get("InitSimEnergyDepositLabel", "undefined")} + , fInitSimEnergyDepositLiteLabel{p.get("InitSimEnergyDepositLiteLabel", "undefined")} , fInitSimPhotonsLabel{p.get("InitSimPhotonsLabel", "undefined")} , fInitWaveformLabel(p.get("InitWaveformLabel", "undefined")) , fShiftAuxDetIDEs{p.get("ShiftAuxDetIDEs", false)} , fShiftBeamGateInfo{p.get("ShiftBeamGateInfo", false)} , fShiftSimEnergyDeposits{p.get("ShiftSimEnergyDeposits", false)} + , fShiftSimEnergyDepositLites{p.get("ShiftSimEnergyDepositLites", false)} , fShiftSimPhotons{p.get("ShiftSimPhotons", false)} , fShiftWaveforms{p.get("ShiftWaveforms", false)} , fAdditionalOffset{p.get("AdditionalOffset", 0.)} { if (!(fShiftSimEnergyDeposits || fShiftSimPhotons || fShiftWaveforms || fShiftAuxDetIDEs || - fShiftBeamGateInfo)) { + fShiftBeamGateInfo || fShiftSimEnergyDepositLites)) { throw art::Exception(art::errors::EventProcessorFailure) << kModuleName << ": NO SHIFTS ENABLED!\n"; } mf::LogInfo(kModuleName) << std::boolalpha << "SHIFTING AUXDETIDES? " << fShiftAuxDetIDEs << '\n' << "SHIFTING BEAMGATEINFO? " << fShiftBeamGateInfo << '\n' << "SHIFTING SIMENERGYDEPOSITS? " << fShiftSimEnergyDeposits << '\n' + << "SHIFTING SIMENERGYDEPOSITLITES? " << fShiftSimEnergyDepositLites << '\n' << "SHIFTING SIMPHOTONS? " << fShiftSimPhotons << '\n' << "SHIFTING OPDETWAVEFORMS? " << fShiftWaveforms; if (fShiftAuxDetIDEs) { produces>(); } if (fShiftBeamGateInfo) { produces>(); } if (fShiftSimEnergyDeposits) { produces>(); } + if (fShiftSimEnergyDepositLites) { produces>(); } if (fShiftSimPhotons) { produces>(); } if (fShiftWaveforms) { produces>(); } } @@ -205,6 +212,24 @@ void AdjustSimForTrigger::produce(art::Event& e) } e.put(std::move(pSimEDeps)); } + // and SimEnergyDepositLite's + if (fShiftSimEnergyDepositLites) { + auto const& simEDepLites = + e.getProduct>(fInitSimEnergyDepositLiteLabel); + + auto pSimEDepLites = std::make_unique>(); + + for (auto const& inSimEDepLite : simEDepLites) { + double energy = inSimEDepLite.Energy(); + geo::Point_t middlePos = inSimEDepLite.Position(); + double middleTime = inSimEDepLite.Time() + timeShiftForTrigger_ns; + int ID = inSimEDepLite.TrackID(); + + pSimEDepLites->emplace_back(energy, middlePos, middleTime, ID); + } + + e.put(std::move(pSimEDepLites)); + } // Repeat for sim::SimPhotons if (fShiftSimPhotons) { diff --git a/sbncode/DetSim/CMakeLists.txt b/sbncode/DetSim/CMakeLists.txt index 113aa375a..805004f36 100644 --- a/sbncode/DetSim/CMakeLists.txt +++ b/sbncode/DetSim/CMakeLists.txt @@ -31,8 +31,8 @@ set( MODULE_LIBRARIES FFTW3::FFTW3 ) -cet_build_plugin(AdjustSimForTrigger art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(FilterSimEnergyDeposits art::module LIBRARIES ${MODULE_LIBRARIES}) +cet_build_plugin(AdjustSimForTrigger art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(FilterSimEnergyDeposits art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) add_subdirectory(fcl) diff --git a/sbncode/DetSim/FilterSimEnergyDeposits_module.cc b/sbncode/DetSim/FilterSimEnergyDeposits_module.cc index ffb45d6e3..bfcd191dd 100644 --- a/sbncode/DetSim/FilterSimEnergyDeposits_module.cc +++ b/sbncode/DetSim/FilterSimEnergyDeposits_module.cc @@ -18,6 +18,7 @@ #include "larcorealg/Geometry/BoxBoundedGeo.h" #include "lardataobj/Simulation/SimEnergyDeposit.h" +#include "lardataobj/Simulation/SimEnergyDepositLite.h" #include @@ -43,6 +44,7 @@ class FilterSimEnergyDeposits : public art::EDProducer { // Declare member data here. art::InputTag fInitSimEnergyDepositLabel; + art::InputTag fInitSimEnergyDepositLiteLabel; geo::BoxBoundedGeo fBox; static constexpr auto kModuleName = "FilterSimEnergyDeposit"; double ShiftX(double z) const; @@ -56,6 +58,7 @@ class FilterSimEnergyDeposits : public art::EDProducer { FilterSimEnergyDeposits::FilterSimEnergyDeposits(fhicl::ParameterSet const& p) : EDProducer{p} , fInitSimEnergyDepositLabel{p.get("InitSimEnergyDepositLabel", "undefined")} + , fInitSimEnergyDepositLiteLabel{p.get("InitSimEnergyDepositLiteLabel", "undefined")} , fBox{p.get("P1_X"), p.get("P2_X"), p.get("P1_Y"), p.get("P2_Y"), p.get("P1_Z"), p.get("P2_Z")} @@ -66,6 +69,8 @@ FilterSimEnergyDeposits::FilterSimEnergyDeposits(fhicl::ParameterSet const& p) // Call appropriate produces<>() functions here. // Call appropriate consumes<>() for any products to be retrieved by this module. produces>(); + + if (fInitSimEnergyDepositLiteLabel != "undefined") { produces>(); } } @@ -126,6 +131,27 @@ void FilterSimEnergyDeposits::produce(art::Event& e) origID)); } e.put(std::move(pSimEDeps)); + + // also futz with the SimEnergyDepositLite's if configured to + if (fInitSimEnergyDepositLiteLabel == "undefined") return; + + auto const& simEDepLites = + e.getProduct>(fInitSimEnergyDepositLiteLabel); + auto pSimEDepLites = std::make_unique>(); + pSimEDepLites->reserve(simEDepLites.size()); + + for (auto const& inSimEDepLite : simEDepLites) { + geo::Point_t middlePos = inSimEDepLite.Position(); + if(fBox.ContainsPosition(middlePos)) + continue; + double energy = inSimEDepLite.Energy(); + double middleTime = inSimEDepLite.Time(); + int ID = inSimEDepLite.TrackID(); + + pSimEDepLites->emplace_back(energy, middlePos, middleTime, ID); + } + + e.put(std::move(pSimEDepLites)); } DEFINE_ART_MODULE(FilterSimEnergyDeposits) diff --git a/sbncode/DetSim/fcl/icarus_simedepfilter.fcl b/sbncode/DetSim/fcl/icarus_simedepfilter.fcl index 124a50981..e56b2db8b 100644 --- a/sbncode/DetSim/fcl/icarus_simedepfilter.fcl +++ b/sbncode/DetSim/fcl/icarus_simedepfilter.fcl @@ -3,6 +3,7 @@ BEGIN_PROLOG simedepfilter_ind1gap: { module_type: "FilterSimEnergyDeposits" InitSimEnergyDepositLabel: "ionization" + InitSimEnergyDepositLiteLabel: "sedlite" P1_X: -400 #cm, create a box that spans the full x,y range. Not super-precise. P1_Y: -200 #cm, create a box that spans the full x,y range. Not super-precise. P1_Z: -2 #cm, one edge of the filtered gap based on the 37 mm total size of the wire gap @@ -16,4 +17,4 @@ simedepfilter_ind1gap: { } -END_PROLOG \ No newline at end of file +END_PROLOG diff --git a/sbncode/EventGenerator/CMakeLists.txt b/sbncode/EventGenerator/CMakeLists.txt index b5329a992..292682726 100644 --- a/sbncode/EventGenerator/CMakeLists.txt +++ b/sbncode/EventGenerator/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(MeVPrtl) add_subdirectory(MultiPart) +add_subdirectory(WireModGen) diff --git a/sbncode/EventGenerator/MeVPrtl/CMakeLists.txt b/sbncode/EventGenerator/MeVPrtl/CMakeLists.txt index a4a480811..3d5655ef2 100644 --- a/sbncode/EventGenerator/MeVPrtl/CMakeLists.txt +++ b/sbncode/EventGenerator/MeVPrtl/CMakeLists.txt @@ -2,7 +2,7 @@ add_subdirectory(Tools) add_subdirectory(config) cet_build_plugin( MeVPrtlGen art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -24,7 +24,7 @@ cet_build_plugin( MeVPrtlGen art::module ) cet_build_plugin( MeVPrtlTestRayTrace art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/ALP/CMakeLists.txt b/sbncode/EventGenerator/MeVPrtl/Tools/ALP/CMakeLists.txt index 5b30f0ab8..a5aabb70f 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/ALP/CMakeLists.txt +++ b/sbncode/EventGenerator/MeVPrtl/Tools/ALP/CMakeLists.txt @@ -5,7 +5,7 @@ art_make_library( LIBRARY_NAME sbncode_EventGenerator_MeVPrtl_ALP_ThreeBodyInteg ) cet_build_plugin( Meson2ALP art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -25,7 +25,7 @@ cet_build_plugin( Meson2ALP art::tool ) cet_build_plugin( ALPMakeDecay art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/BNBKaonGen_tool.cc b/sbncode/EventGenerator/MeVPrtl/Tools/BNBKaonGen_tool.cc index 6d4ed4711..b5d89027f 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/BNBKaonGen_tool.cc +++ b/sbncode/EventGenerator/MeVPrtl/Tools/BNBKaonGen_tool.cc @@ -311,7 +311,7 @@ simb::MCFlux BNBKaonGen::MakeMCFlux(const bsim::BooNe &boone) { << std::endl; } - flux.fFluxType = simb::kDk2Nu; + flux.fFluxType = simb::kNtuple; flux.fnimpwt = fBooneNtp.beamwgt; flux.fvx = fBooneNtp.ini_pos[0][0]; //0 flux.fvy = fBooneNtp.ini_pos[0][1]; //0 diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/CMakeLists.txt b/sbncode/EventGenerator/MeVPrtl/Tools/CMakeLists.txt index e385350a6..b82d2ca4f 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/CMakeLists.txt +++ b/sbncode/EventGenerator/MeVPrtl/Tools/CMakeLists.txt @@ -11,7 +11,7 @@ art_make_library( LIBRARY_NAME sbncode_EventGenerator_MeVPrtl_Constants cet_build_plugin( EmptyKaonGen art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -34,7 +34,7 @@ cet_build_plugin( EmptyKaonGen art::tool cet_build_plugin( NuMiKaonGen art::tool - LIBRARIES + LIBRARIES PRIVATE ifdh_art::IFDH_service art::Framework_Core art::Framework_Services_Registry @@ -57,7 +57,7 @@ cet_build_plugin( NuMiKaonGen art::tool ) cet_build_plugin( TxtFileGen art::tool - LIBRARIES + LIBRARIES PRIVATE ifdh_art::IFDH_service art::Framework_Core art::Framework_Services_Registry @@ -80,8 +80,7 @@ cet_build_plugin( TxtFileGen art::tool ) cet_build_plugin( BNBKaonGen art::tool - - LIBRARIES + LIBRARIES PRIVATE ifdh_art::IFDH_service art::Framework_Core art::Framework_Services_Registry @@ -104,7 +103,7 @@ cet_build_plugin( BNBKaonGen art::tool ) cet_build_plugin( WeightedRayTraceBox art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -127,7 +126,7 @@ cet_build_plugin( WeightedRayTraceBox art::tool ) cet_build_plugin( MixedWeightRayTraceBox art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -153,7 +152,7 @@ cet_build_plugin( MixedWeightRayTraceBox art::tool cet_build_plugin( ReThrowRayTraceBox art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -175,7 +174,7 @@ cet_build_plugin( ReThrowRayTraceBox art::tool ) cet_build_plugin( RayTraceBox art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/HNL/CMakeLists.txt b/sbncode/EventGenerator/MeVPrtl/Tools/HNL/CMakeLists.txt index afc82c2b6..2232fdb22 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/HNL/CMakeLists.txt +++ b/sbncode/EventGenerator/MeVPrtl/Tools/HNL/CMakeLists.txt @@ -45,7 +45,7 @@ art_make_library( LIBRARY_NAME sbncode_EventGenerator_MeVPrtl_AnTwoBD cet_build_plugin( Kaon2HNLFlux art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -65,7 +65,7 @@ cet_build_plugin( Kaon2HNLFlux art::tool ) cet_build_plugin( Tau2HNLFlux art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -86,7 +86,7 @@ cet_build_plugin( Tau2HNLFlux art::tool ) cet_build_plugin( HNLMakeDecay art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/CMakeLists.txt b/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/CMakeLists.txt index 867e62a1f..314e72967 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/CMakeLists.txt +++ b/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/CMakeLists.txt @@ -1,5 +1,5 @@ cet_build_plugin( MonoEnergyHiggsFlux art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -17,7 +17,7 @@ cet_build_plugin( MonoEnergyHiggsFlux art::tool cet_build_plugin( Kaon2HiggsFlux art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -37,7 +37,7 @@ cet_build_plugin( Kaon2HiggsFlux art::tool ) cet_build_plugin( HiggsMakeDecay art::tool - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/HiggsMakeDecay_tool.cc b/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/HiggsMakeDecay_tool.cc index 8de728c27..172e42cf2 100644 --- a/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/HiggsMakeDecay_tool.cc +++ b/sbncode/EventGenerator/MeVPrtl/Tools/Higgs/HiggsMakeDecay_tool.cc @@ -82,6 +82,7 @@ class HiggsMakeDecay : public IMeVPrtlDecay { bool fAllowMuonDecay; bool fAllowPionDecay; bool fAllowPi0Decay; + bool fNewFormFactor; bool fAddTimeOfFlight; }; @@ -127,24 +128,34 @@ double MuonPartialWidth(double higs_mass, double mixing) { return LeptonPartialWidth(Constants::Instance().muon_mass, higs_mass, mixing); } -double PionPartialWidth(double pion_mass, double higs_mass, double mixing) { +double PionPartialWidth(double pion_mass, double higs_mass, double mixing, bool use_new_ff) { if (pion_mass * 2 >= higs_mass) return 0.; double higgs_vev = Constants::Instance().higgs_vev; - double form_factor = (2. / 9.) * higs_mass * higs_mass + (11. / 9.) * pion_mass * pion_mass; + double form_factor = 0.; + if (use_new_ff) { + // New, improved form factor based on the plot in arXiv:1909.11670v4 + // This form factor is significnatly different than in older versions + // The expression is a fit to the Fig. 1 left panel in arXiv:1909.11670v4 + form_factor = 0.537569 * pow(higs_mass - 2 * Constants::Instance().pizero_mass,0.75); + } + else { + // Old form factor + form_factor = (2. / 9.) * higs_mass * higs_mass + (11. / 9.) * pion_mass * pion_mass; + } double width = (mixing * mixing * 3 * form_factor * form_factor / (32 * M_PI * higgs_vev * higgs_vev * higs_mass)) * pow(1- 4. * pion_mass * pion_mass / (higs_mass * higs_mass), 1. / 2.); return width; } -double PiPlusPartialWidth(double higs_mass, double mixing) { - return PionPartialWidth(Constants::Instance().piplus_mass, higs_mass, mixing); +double PiPlusPartialWidth(double higs_mass, double mixing, bool use_new_ff) { + return 2*PionPartialWidth(Constants::Instance().piplus_mass, higs_mass, mixing, use_new_ff); } -double PiZeroPartialWidth(double higs_mass, double mixing) { - return PionPartialWidth(Constants::Instance().pizero_mass, higs_mass, mixing); +double PiZeroPartialWidth(double higs_mass, double mixing, bool use_new_ff) { + return PionPartialWidth(Constants::Instance().pizero_mass, higs_mass, mixing, use_new_ff); } HiggsMakeDecay::HiggsMakeDecay(fhicl::ParameterSet const &pset): @@ -173,6 +184,7 @@ void HiggsMakeDecay::configure(fhicl::ParameterSet const &pset) fAllowMuonDecay = pset.get("AllowMuonDecay", true); fAllowPionDecay = pset.get("AllowPionDecay", true); fAllowPi0Decay = pset.get("AllowPi0Decay", true); + fNewFormFactor = pset.get("NewFormFactor", true); fAddTimeOfFlight = pset.get("AddTimeOfFlight", true); if (fReferenceHiggsEnergy < 0. && fReferenceHiggsKaonEnergy > 0.) { @@ -185,8 +197,8 @@ void HiggsMakeDecay::configure(fhicl::ParameterSet const &pset) // Get each partial width double width_elec = ElectronPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing); double width_muon = MuonPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing); - double width_piplus = PiPlusPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing); - double width_pizero = PiZeroPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing); + double width_piplus = PiPlusPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing, fNewFormFactor); + double width_pizero = PiZeroPartialWidth(fReferenceHiggsMass, fReferenceHiggsMixing, fNewFormFactor); // total lifetime double lifetime_ns = Constants::Instance().hbar / (width_elec + width_muon + width_piplus + width_pizero); @@ -241,8 +253,8 @@ bool HiggsMakeDecay::Decay(const MeVPrtlFlux &flux, const TVector3 &in, const TV // Get each partial width double width_elec = ElectronPartialWidth(flux.mass, mixing); double width_muon = MuonPartialWidth(flux.mass, mixing); - double width_piplus = PiPlusPartialWidth(flux.mass, mixing); - double width_pizero = PiZeroPartialWidth(flux.mass, mixing); + double width_piplus = PiPlusPartialWidth(flux.mass, mixing, fNewFormFactor); + double width_pizero = PiZeroPartialWidth(flux.mass, mixing, fNewFormFactor); // total lifetime double lifetime_ns = Constants::Instance().hbar / (width_elec + width_muon + width_piplus + width_pizero); diff --git a/sbncode/EventGenerator/MeVPrtl/config/Higgs/dissonant_higgs.fcl b/sbncode/EventGenerator/MeVPrtl/config/Higgs/dissonant_higgs.fcl index eb91ecdd0..bfb5bb05b 100644 --- a/sbncode/EventGenerator/MeVPrtl/config/Higgs/dissonant_higgs.fcl +++ b/sbncode/EventGenerator/MeVPrtl/config/Higgs/dissonant_higgs.fcl @@ -41,6 +41,7 @@ decay_higgs: { ReferenceRayLength: 2100 # 21m ReferenceHiggsMass: @local::higgsM ReferenceHiggsMixing: 1e-5 + NewFormFactor: true ReferenceRayDistance: 80000 # 800m ReferenceHiggsEnergyFromKaonEnergy: 15. # GeV AddTimeOfFlight: true diff --git a/sbncode/EventGenerator/MultiPart/CMakeLists.txt b/sbncode/EventGenerator/MultiPart/CMakeLists.txt index 752ac5ed7..47833dbee 100644 --- a/sbncode/EventGenerator/MultiPart/CMakeLists.txt +++ b/sbncode/EventGenerator/MultiPart/CMakeLists.txt @@ -1,69 +1,3 @@ -cet_build_plugin( MultiPartRain art::module - LIBRARIES - larcorealg::Geometry - larcore::Geometry_Geometry_service - lardata::Utilities - larevt::Filters - lardataobj::RawData - larevt::CalibrationDBI_IOVData - larevt::CalibrationDBI_Providers - lardataobj::RecoBase - lardata::ArtDataHelper - larsim::Simulation nug4::ParticleNavigation - lardataobj::Simulation - nusimdata::SimulationBase - lardata::Utilities - lardataobj::MCBase - larcoreobj::SummaryData - nusimdata::SimulationBase - nurandom::RandomUtils_NuRandomService_service - lardataobj::MCBase - CLHEP::CLHEP - art::Framework_Core - art::Framework_Principal - art::Framework_Services_Registry - art_root_io::tfile_support ROOT::Core - art_root_io::TFileService_service - art::Persistency_Common - art::Persistency_Provenance - art::Utilities - canvas::canvas - ROOT::EG - ) - -cet_build_plugin( MultiPartVertex art::module - LIBRARIES - larcorealg::Geometry - larcore::Geometry_Geometry_service - lardata::Utilities - larevt::Filters - lardataobj::RawData - larevt::CalibrationDBI_IOVData - larevt::CalibrationDBI_Providers - lardataobj::RecoBase - lardata::ArtDataHelper - larsim::Simulation nug4::ParticleNavigation - lardataobj::Simulation - nusimdata::SimulationBase - lardata::Utilities - lardataobj::MCBase - larcoreobj::SummaryData - nusimdata::SimulationBase - nurandom::RandomUtils_NuRandomService_service - lardataobj::MCBase - CLHEP::CLHEP - art::Framework_Core - art::Framework_Principal - art::Framework_Services_Registry - art_root_io::tfile_support ROOT::Core - art_root_io::TFileService_service - art::Persistency_Common - art::Persistency_Provenance - art::Utilities - canvas::canvas - ROOT::EG - ) - install_headers() install_fhicl() install_source() diff --git a/sbncode/EventGenerator/MultiPart/MultiPartRain_module.cc b/sbncode/EventGenerator/MultiPart/MultiPartRain_module.cc deleted file mode 100644 index 303746118..000000000 --- a/sbncode/EventGenerator/MultiPart/MultiPartRain_module.cc +++ /dev/null @@ -1,521 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// Class: MultiPartRain -// Module Type: producer -// File: MultiPartRain_module.cc -// -// Generated at Tue Dec 13 15:48:59 2016 by Kazuhiro Terao using artmod -// from cetpkgsupport v1_11_00. -//////////////////////////////////////////////////////////////////////// - -#include "art/Framework/Core/EDProducer.h" -#include "art/Framework/Core/ModuleMacros.h" -#include "art/Framework/Principal/Event.h" -#include "art/Framework/Principal/Handle.h" -#include "art/Framework/Principal/Run.h" -#include "art/Framework/Principal/SubRun.h" -//#include "art/Utilities/InputTag.h" -#include "fhiclcpp/ParameterSet.h" -#include "messagefacility/MessageLogger/MessageLogger.h" - -#include -#include -#include -#include "cetlib/pow.h" - -#include "CLHEP/Random/RandFlat.h" -#include "CLHEP/Random/RandGauss.h" -#include "CLHEP/Random/RandGeneral.h" - -#include "TRandom.h" -#include "nurandom/RandomUtils/NuRandomService.h" -#include "larcore/Geometry/Geometry.h" -#include "larcore/CoreUtils/ServiceUtil.h" -#include "larcoreobj/SummaryData/RunData.h" -#include "larcoreobj/SimpleTypesAndConstants/PhysicalConstants.h" - -#include "nusimdata/SimulationBase/MCTruth.h" -#include "nusimdata/SimulationBase/MCParticle.h" - -#include "TLorentzVector.h" -#include "TDatabasePDG.h" - -struct PartGenParam { - std::vector pdg; - std::vector mass; - std::array multi; - std::array kerange; - bool use_mom; - bool direct_inward; - double weight; -}; - -class MultiPartRain; - -class MultiPartRain : public art::EDProducer { -public: - explicit MultiPartRain(fhicl::ParameterSet const & p); - // The destructor generated by the compiler is fine for classes - // without bare pointers or other resource use. - ~MultiPartRain(); - - // Plugins should not be copied or assigned. - MultiPartRain(MultiPartRain const &) = delete; - MultiPartRain(MultiPartRain &&) = delete; - MultiPartRain & operator = (MultiPartRain const &) = delete; - MultiPartRain & operator = (MultiPartRain &&) = delete; - - // Required functions. - void produce(art::Event & e) override; - - void beginRun(art::Run& run) override; - - void GenPosition(double& x, double& y, double& z); - - std::array extractDirection() const; - void GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz, - const double x, const double y, const double z); - - std::vector GenParticles() const; - -private: - - CLHEP::HepRandomEngine& fFlatEngine; - std::unique_ptr fFlatRandom; - std::unique_ptr fNormalRandom; - std::unique_ptr fCosmicAngleRandom; - - // exception thrower - void abort(const std::string msg) const; - - // array of particle info for generation - std::vector _param_v; - - // g4 time of generation - double _t0; - double _t0_sigma; - - // g4 position - std::array _xrange; - std::array _yrange; - std::array _zrange; - - // Whether to use uniform or cosmic-like angle distribution - bool _cosmic_distribution; - - // TPC array - std::vector > _tpc_v; - - // multiplicity constraint - size_t _multi_min; - size_t _multi_max; - - // verbosity flag - unsigned short _debug; -}; - -void MultiPartRain::abort(const std::string msg) const -{ - std::cerr << "\033[93m" << msg.c_str() << "\033[00m" << std::endl; - throw std::exception(); -} - -MultiPartRain::~MultiPartRain() -{ } - -MultiPartRain::MultiPartRain(fhicl::ParameterSet const & p) -: EDProducer(p) -, fFlatEngine(art::ServiceHandle()->registerAndSeedEngine( - createEngine(0, "HepJamesRandom", "GenRain"), "HepJamesRandom", "GenRain")) - //, fFlatEngine(art::ServiceHandle()->createEngine(*this, "HepJamesRandom", "Gen", p, "Seed")) -// : -// Initialize member data here. -{ - - // - // Random engine initialization - // - fFlatRandom = std::make_unique(fFlatEngine,0,1); - fNormalRandom = std::make_unique(fFlatEngine); - - // x^2 distribution for fCosmicAngleRandom - // will be used to draw cos(theta) - const int nbins(100); - double parent[nbins]; - for (size_t idx = 0; idx < nbins; ++idx) { - //parent[idx] = cet::square(cos(((float) idx)/nbins * util::pi())); - parent[idx] = cet::square(((float) idx)/nbins); - } - fCosmicAngleRandom = std::make_unique(fFlatEngine, parent, nbins); - - produces< std::vector >(); - produces< sumdata::RunData, art::InRun >(); - - _debug = p.get("DebugMode",0); - - _t0 = p.get("G4Time"); - _t0_sigma = p.get("G4TimeJitter"); - if(_t0_sigma < 0) this->abort("Cannot have a negative value for G4 time jitter"); - - _multi_min = p.get("MultiMin"); - _multi_max = p.get("MultiMax"); - _cosmic_distribution = p.get("CosmicDistribution", false); - - _tpc_v = p.get > >("TPCRange"); - auto const xrange = p.get > ("XRange"); - auto const yrange = p.get > ("YRange"); - auto const zrange = p.get > ("ZRange"); - auto const part_cfg = p.get("ParticleParameter"); - - _param_v.clear(); - auto const pdg_v = part_cfg.get > > ("PDGCode"); - auto const minmult_v = part_cfg.get > ("MinMulti"); - auto const maxmult_v = part_cfg.get > ("MaxMulti"); - auto const weight_v = part_cfg.get > ("ProbWeight"); - - auto kerange_v = part_cfg.get > > ("KERange"); - auto momrange_v = part_cfg.get > > ("MomRange"); - - if( (kerange_v.empty() && momrange_v.empty()) || - (!kerange_v.empty() && !momrange_v.empty()) ) { - this->abort("Only one of KERange or MomRange must be empty!"); - } - - bool use_mom = false; - if(kerange_v.empty()){ - kerange_v = momrange_v; - use_mom = true; - } - // sanity check - if( pdg_v.size() != kerange_v.size() || - pdg_v.size() != minmult_v.size() || - pdg_v.size() != maxmult_v.size() || - pdg_v.size() != weight_v.size() ) - this->abort("configuration parameters have incompatible lengths!"); - - // further sanity check (1 more depth for some double-array) - for(auto const& r : pdg_v ) { if( r.empty() ) this->abort("PDG code not given!"); } - for(auto const& r : kerange_v) { if( r.size()!=2) this->abort("Incompatible legnth @ KE vector!"); } - - for(size_t idx=0; idx maxmult_v[idx]) this->abort("Particle MinMulti > Particle MaxMulti!"); - if(minmult_v[idx] > _multi_max) this->abort("Particle MinMulti > overall MultiMax!"); - if(minmult_v[idx] > _multi_min) - _multi_min = minmult_v[idx]; - } - if(_multi_max < _multi_min) this->abort("Overall MultiMax <= overall MultiMin!"); - - if(!xrange.empty() && xrange.size() >2) this->abort("Incompatible legnth @ X vector!" ); - if(!yrange.empty() && yrange.size() >2) this->abort("Incompatible legnth @ Y vector!" ); - if(!zrange.empty() && zrange.size() >2) this->abort("Incompatible legnth @ Z vector!" ); - - // slight modification from mpv: define the overall volume across specified TPC IDs + range options - double xmin,xmax,ymin,ymax,zmin,zmax; - xmin = ymin = zmin = 1.e20; - xmax = ymax = zmax = -1.e20; - // Implementation of required member function here. - auto geop = lar::providerFrom(); - for(auto const& tpc_id : _tpc_v) { - assert(tpc_id.size() == 2); - size_t cid = tpc_id[0]; - size_t tid = tpc_id[1]; - auto const& cryostat = geop->Cryostat(geo::CryostatID(cid)); - assert(cryostat.HasTPC(tid)); - - auto const& tpc = cryostat.TPC(tid); - auto const& tpcabox = tpc.ActiveBoundingBox(); - xmin = std::min(tpcabox.MinX(), xmin); - ymin = std::min(tpcabox.MinY(), ymin); - zmin = std::min(tpcabox.MinZ(), zmin); - xmax = std::max(tpcabox.MaxX(), xmax); - ymax = std::max(tpcabox.MaxY(), ymax); - zmax = std::max(tpcabox.MaxZ(), zmax); - - if(_debug) { - std::cout << "Using Cryostat " << tpc_id[0] << " TPC " << tpc_id[1] - << " ... X " << xmin << " => " << xmax - << " ... Y " << ymin << " => " << ymax - << " ... Z " << zmin << " => " << zmax - << std::endl; - } - } - - // range register - if(xrange.size()==1) { _xrange[0] = _xrange[1] = xrange[0]; } - if(yrange.size()==1) { _yrange[0] = _yrange[1] = yrange[0]; } - if(zrange.size()==1) { _zrange[0] = _zrange[1] = zrange[0]; } - if(xrange.size()==2) { _xrange[0] = xrange[0]; _xrange[1] = xrange[1]; } - if(yrange.size()==2) { _yrange[0] = yrange[0]; _yrange[1] = yrange[1]; } - if(zrange.size()==2) { _zrange[0] = zrange[0]; _zrange[1] = zrange[1]; } - - _xrange[0] = xmin + _xrange[0]; - _xrange[1] = xmax - _xrange[1]; - _yrange[0] = ymin + _yrange[0]; - _yrange[1] = ymax - _yrange[1]; - _zrange[0] = zmin + _zrange[0]; - _zrange[1] = zmax - _zrange[1]; - - // check - assert(_xrange[0] <= _xrange[1]); - assert(_yrange[0] <= _yrange[1]); - assert(_zrange[0] <= _zrange[1]); - - if(_debug>0) { - std::cout<<"Particle generation world boundaries..."< " << _xrange[1] << std::endl - <<"Y " << _yrange[0] << " => " << _yrange[1] << std::endl - <<"Z " << _zrange[0] << " => " << _zrange[1] << std::endl; - } - - - // register - //auto db = new TDatabasePDG; - auto db = TDatabasePDG::Instance(); - bool direct_inward = p.get("DirectInward", true); - if (_cosmic_distribution && direct_inward) { - std::cout << "Cannot satisfy both CosmicDistribution and Inward Directioning at the same time" << std::endl; - throw std::exception(); - } - for(size_t idx=0; idxGetParticle(param.pdg[i])->Mass(); - - // sanity check - if(kerange[0]<0 || kerange[1]<0) - this->abort("You provided negative energy? Fuck off Mr. Trump."); - - // overall range check - if(param.kerange[0] > param.kerange[1]) this->abort("KE range has no phase space..."); - - if(_debug>0) { - std::cout << "Generating particle (PDG"; - for(auto const& pdg : param.pdg) std::cout << " " << pdg; - std::cout << ")" << std::endl - << (param.use_mom ? " KE range ....... " : " Mom range ....... ") - << param.kerange[0] << " => " << param.kerange[1] << " MeV" << std::endl - << std::endl; - } - - _param_v.push_back(param); - } -} - -void MultiPartRain::beginRun(art::Run& run) -{ - // grab the geometry object to see what geometry we are using - art::ServiceHandle geo; - - std::unique_ptr runData(new sumdata::RunData(geo->DetectorName())); - - run.put(std::move(runData), art::fullRun()); - - return; -} - -std::vector MultiPartRain::GenParticles() const { - - std::vector result; - std::vector gen_count_v(_param_v.size(),0); - std::vector weight_v(_param_v.size(),0); - for(size_t idx=0; idx<_param_v.size(); ++idx) - weight_v[idx] = _param_v[idx].weight; - - int num_part = (int)(fFlatRandom->fire(_multi_min,_multi_max+1-1.e-10)); - - while(num_part) { - - double total_weight = 0; - for(auto const& v : weight_v) total_weight += v; - - double rval = 0; - rval = fFlatRandom->fire(0,total_weight); - - size_t idx = 0; - for(idx = 0; idx < weight_v.size(); ++idx) { - rval -= weight_v[idx]; - if(rval <=0.) break; - } - - // register to the output - result.push_back(idx); - - // if generation count exceeds max, set probability weight to be 0 - gen_count_v[idx] += 1; - if(gen_count_v[idx] >= _param_v[idx].multi[1]) - weight_v[idx] = 0.; - - --num_part; - } - return result; -} - -void MultiPartRain::GenPosition(double& x, double& y, double& z) { - - x = fFlatRandom->fire(_xrange[0],_xrange[1]); - y = fFlatRandom->fire(_yrange[0],_yrange[1]); - z = fFlatRandom->fire(_zrange[0],_zrange[1]); - - if(_debug>0) { - std::cout << "Generating a rain particle at (" - << x << "," << y << "," << z << ")" << std::endl; - } -} - -std::array MultiPartRain::extractDirection() const { - double px, py, pz; - if (_cosmic_distribution) { - double phi = fFlatRandom->fire(0, 2 * util::pi()); - // Zenith Angle Theta is in [pi/2, pi] - double costheta = - 1. * fCosmicAngleRandom->fire(); - double sintheta = sqrt(1 - costheta * costheta); - pz = cos(phi) * sintheta; - px = sin(phi) * sintheta; - py = costheta; - } else { - px = fNormalRandom->fire(0, 1); - py = fNormalRandom->fire(0, 1); - pz = fNormalRandom->fire(0, 1); - double p = std::hypot(px, py, pz); - px = px / p; - py = py / p; - pz = pz / p; - } - - std::array result = {px, py, pz}; - return result; -} - -void MultiPartRain::GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz, - const double x, const double y, const double z) { - - double tot_energy = 0; - if(param.use_mom) - tot_energy = sqrt(cet::square(fFlatRandom->fire(param.kerange[0],param.kerange[1])) + cet::square(mass)); - else - tot_energy = fFlatRandom->fire(param.kerange[0],param.kerange[1]) + mass; - - double mom_mag = sqrt(cet::square(tot_energy) - cet::square(mass)); - - /* Generating unit vector with uniform distribution - * in direction = over the sphere. - * - * It is sufficient to draw a normal variable in - * each direction and normalize. - * - * https://mathworld.wolfram.com/SpherePointPicking.html - */ - - if(!param.direct_inward) { - std::cout<<"No inward directioning..."< p = extractDirection(); - px = p[0]; py = p[1]; pz = p[2]; - }else{ - double sign_x = ( (x - _xrange[0]) < (_xrange[1] - x) ? 1.0 : -1.0 ); - double sign_y = ( (y - _yrange[0]) < (_yrange[1] - y) ? 1.0 : -1.0 ); - double sign_z = ( (z - _zrange[0]) < (_zrange[1] - z) ? 1.0 : -1.0 ); - - if(_debug) { - std::cout<<"Generating XYZ direction sign for a particle at (" << x << "," << y << "," << z << ")" << std::endl - <<"X: " << _xrange[0] << " => " << _xrange[1] << " ... " << sign_x << std::endl - <<"Y: " << _yrange[0] << " => " << _yrange[1] << " ... " << sign_y << std::endl - <<"Z: " << _zrange[0] << " => " << _zrange[1] << " ... " << sign_z << std::endl - << std::endl; - } - - while(1) { - std::array p = extractDirection(); - px = p[0]; py = p[1]; pz = p[2]; - if( (px * sign_x) >= 0. && - (py * sign_y) >= 0. && - (pz * sign_z) >= 0. ) - break; - } - - } - //std::cout<<"LOGME,"<1) - std::cout << " Direction : (" << px << "," << py << "," << pz << ")" << std::endl - << " Momentum : " << mom_mag << " [MeV/c]" << std::endl - << " Energy : " << tot_energy << " [MeV/c^2]" << std::endl; - px *= mom_mag; - py *= mom_mag; - pz *= mom_mag; - -} - -void MultiPartRain::produce(art::Event & e) -{ - if(_debug>0) std::cout << "Processing a new event..." << std::endl; - - std::unique_ptr< std::vector > mctArray(new std::vector); - - simb::MCTruth mct; - - mct.SetOrigin(simb::kCosmicRay); - - std::vector part_v; - - auto const param_idx_v = GenParticles(); - if(_debug) - std::cout << "Generating" << param_idx_v.size() << " particles..." << std::endl; - - for(size_t idx=0; idxfire(0,param.pdg.size()-1.e-10)); - auto const& pdg = param.pdg[pdg_index]; - auto const& mass = param.mass[pdg_index]; - if(_debug) std::cout << " " << idx << "th instance PDG " << pdg << std::endl; - double x, y, z; - GenPosition(x,y,z); - double g4_time = fFlatRandom->fire(_t0 - _t0_sigma/2., _t0 + _t0_sigma/2.); - TLorentzVector pos(x,y,z,g4_time); - std::cout<<"Generating momentum..."<push_back(mct); - - e.put(std::move(mctArray)); -} - -DEFINE_ART_MODULE(MultiPartRain) diff --git a/sbncode/EventGenerator/MultiPart/MultiPartVertex_module.cc b/sbncode/EventGenerator/MultiPart/MultiPartVertex_module.cc deleted file mode 100644 index 5a4e3cf08..000000000 --- a/sbncode/EventGenerator/MultiPart/MultiPartVertex_module.cc +++ /dev/null @@ -1,709 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// Class: MultiPartVertex -// Module Type: producer -// File: MultiPartVertex_module.cc -// -// Generated at Tue Dec 13 15:48:59 2016 by Kazuhiro Terao using artmod -// from cetpkgsupport v1_11_00. -//////////////////////////////////////////////////////////////////////// - -#include "art/Framework/Core/EDProducer.h" -#include "art/Framework/Core/ModuleMacros.h" -#include "art/Framework/Principal/Event.h" -#include "art/Framework/Principal/Handle.h" -#include "art/Framework/Principal/Run.h" -#include "art/Framework/Principal/SubRun.h" -//#include "art/Utilities/InputTag.h" -#include "fhiclcpp/ParameterSet.h" -#include "messagefacility/MessageLogger/MessageLogger.h" - -#include -#include -#include "cetlib/pow.h" - -#include "CLHEP/Random/RandFlat.h" -#include "CLHEP/Random/RandGauss.h" -#include "TRandom.h" -#include "nurandom/RandomUtils/NuRandomService.h" -#include "larcore/Geometry/Geometry.h" -#include "larcore/CoreUtils/ServiceUtil.h" -#include "larcoreobj/SummaryData/RunData.h" -#include "larcoreobj/SimpleTypesAndConstants/PhysicalConstants.h" - -#include "nusimdata/SimulationBase/MCTruth.h" -#include "nusimdata/SimulationBase/MCParticle.h" - -#include "TLorentzVector.h" -#include "TDatabasePDG.h" - -struct PartGenParam { - std::vector pdg; - std::vector mass; - std::array multi; - std::array kerange; - bool use_mom; - double weight; -}; - -class MultiPartVertex; - -class MultiPartVertex : public art::EDProducer { -public: - explicit MultiPartVertex(fhicl::ParameterSet const & p); - // The destructor generated by the compiler is fine for classes - // without bare pointers or other resource use. - ~MultiPartVertex(); - - // Plugins should not be copied or assigned. - MultiPartVertex(MultiPartVertex const &) = delete; - MultiPartVertex(MultiPartVertex &&) = delete; - MultiPartVertex & operator = (MultiPartVertex const &) = delete; - MultiPartVertex & operator = (MultiPartVertex &&) = delete; - - // Required functions. - void produce(art::Event & e) override; - - void beginRun(art::Run& run) override; - - void GenPosition(double& x, double& y, double& z); - void GenBoost(double& bx, double& by, double& bz); - - std::array extractDirection() const; - void GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz); - void GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz, bool& same_range); - - void GenMomentumSF(const double& sf, const double& mass, const double& p, double &p_sf); - std::vector GenParticles() const; - -private: - - CLHEP::HepRandomEngine& fFlatEngine; - std::unique_ptr fFlatRandom; - std::unique_ptr fNormalRandom; - - // exception thrower - void abort(const std::string msg) const; - - // array of particle info for generation - std::vector _param_v; - - // g4 time of generation - double _t0; - double _t0_sigma; - - // g4 position - std::array _xrange; - std::array _yrange; - std::array _zrange; - - // TPC range - std::vector > _tpc_v; - - // multiplicity constraint - size_t _multi_min; - size_t _multi_max; - - // Range of boosts - std::array _gamma_beta_range; - - // verbosity flag - unsigned short _debug; - - unsigned short _use_boost; - - unsigned short _revert; -}; - -void MultiPartVertex::abort(const std::string msg) const -{ - std::cerr << "\033[93m" << msg.c_str() << "\033[00m" << std::endl; - throw std::exception(); -} - -MultiPartVertex::~MultiPartVertex() -{ } - -MultiPartVertex::MultiPartVertex(fhicl::ParameterSet const & p) - : EDProducer(p) - , fFlatEngine(art::ServiceHandle()->registerAndSeedEngine( - createEngine(0, "HepJamesRandom", "GenVertex"), "HepJamesRandom", "GenVertex")) - //, fFlatEngine(art::ServiceHandle()->createEngine(*this, "HepJamesRandom", "Gen", p, "Seed")) - // Initialize member data here. -{ - - // - // Random engine initialization - // - fFlatRandom = std::make_unique(fFlatEngine,0,1); - fNormalRandom = std::make_unique(fFlatEngine); - - produces< std::vector >(); - produces< sumdata::RunData, art::InRun >(); - - _debug = p.get("DebugMode",0); - _revert = p.get("Revert",0); - _use_boost = p.get("UseBoost",0); - _t0 = p.get("G4Time"); - _t0_sigma = p.get("G4TimeJitter"); - if(_t0_sigma < 0) this->abort("Cannot have a negative value for G4 time jitter"); - - _multi_min = p.get("MultiMin"); - _multi_max = p.get("MultiMax"); - - _tpc_v = p.get > >("TPCRange"); - auto const xrange = p.get > ("XRange"); - auto const yrange = p.get > ("YRange"); - auto const zrange = p.get > ("ZRange"); - - auto const part_cfg = p.get("ParticleParameter"); - _gamma_beta_range = p.get >("GammaBetaRange", {0.0,0.0}); // _gamma_beta denotes gamma * beta - - _param_v.clear(); - auto const pdg_v = part_cfg.get > > ("PDGCode"); - auto const minmult_v = part_cfg.get > ("MinMulti"); - auto const maxmult_v = part_cfg.get > ("MaxMulti"); - auto const weight_v = part_cfg.get > ("ProbWeight"); - - auto kerange_v = part_cfg.get > > ("KERange"); - auto momrange_v = part_cfg.get > > ("MomRange"); - - if( (kerange_v.empty() && momrange_v.empty()) || - (!kerange_v.empty() && !momrange_v.empty()) ) { - this->abort("Only one of KERange or MomRange must be empty!"); - } - - bool use_mom = false; - if(kerange_v.empty()){ - kerange_v = momrange_v; - use_mom = true; - } - // sanity check - if( pdg_v.size() != kerange_v.size() || - pdg_v.size() != minmult_v.size() || - pdg_v.size() != maxmult_v.size() || - pdg_v.size() != weight_v.size() ) - this->abort("configuration parameters have incompatible lengths!"); - - // further sanity check (1 more depth for some double-array) - for(auto const& r : pdg_v ) { if( r.empty() ) this->abort("PDG code not given!"); } - for(auto const& r : kerange_v) { if( r.size()!=2) this->abort("Incompatible legnth @ KE vector!"); } - if(_gamma_beta_range[0] > _gamma_beta_range[1]) this->abort("Incompatible boost range!"); - - size_t multi_min = 0; - for(size_t idx=0; idx maxmult_v[idx]) this->abort("Particle MinMulti > Particle MaxMulti!"); - if(minmult_v[idx] > _multi_max) this->abort("Particle MinMulti > overall MultiMax!"); - multi_min += minmult_v[idx]; - } - _multi_min = std::max(_multi_min, multi_min); - if(_multi_max < _multi_min) this->abort("Overall MultiMax <= overall MultiMin!"); - - /* - if(!xrange.empty() && xrange.size() >2) this->abort("Incompatible legnth @ X vector!" ); - if(!yrange.empty() && yrange.size() >2) this->abort("Incompatible legnth @ Y vector!" ); - if(!zrange.empty() && zrange.size() >2) this->abort("Incompatible legnth @ Z vector!" ); - - // range register - if(xrange.size()==1) { _xrange[0] = _xrange[1] = xrange[0]; } - if(xrange.size()==2) { _xrange[0] = xrange[0]; _xrange[1] = xrange[1]; } - if(yrange.size()==1) { _yrange[0] = _yrange[1] = yrange[0]; } - if(yrange.size()==2) { _yrange[0] = yrange[0]; _yrange[1] = yrange[1]; } - if(zrange.size()==1) { _zrange[0] = _zrange[1] = zrange[0]; } - if(zrange.size()==2) { _zrange[0] = zrange[0]; _zrange[1] = zrange[1]; } - */ - - if(!xrange.empty() && xrange.size() >2) this->abort("Incompatible legnth @ X vector!" ); - if(!yrange.empty() && yrange.size() >2) this->abort("Incompatible legnth @ Y vector!" ); - if(!zrange.empty() && zrange.size() >2) this->abort("Incompatible legnth @ Z vector!" ); - - // slight modification from mpv: define the overall volume across specified TPC IDs + range options - double xmin,xmax,ymin,ymax,zmin,zmax; - xmin = ymin = zmin = 1.e20; - xmax = ymax = zmax = -1.e20; - // Implementation of required member function here. - auto geop = lar::providerFrom(); - for(auto const& tpc_id : _tpc_v) { - assert(tpc_id.size() == 2); - size_t cid = tpc_id[0]; - size_t tid = tpc_id[1]; - auto const& cryostat = geop->Cryostat(geo::CryostatID(cid)); - assert(cryostat.HasTPC(tid)); - - auto const& tpc = cryostat.TPC(tid); - auto const& tpcabox = tpc.ActiveBoundingBox(); - xmin = std::min(tpcabox.MinX(), xmin); - ymin = std::min(tpcabox.MinY(), ymin); - zmin = std::min(tpcabox.MinZ(), zmin); - xmax = std::max(tpcabox.MaxX(), xmax); - ymax = std::max(tpcabox.MaxY(), ymax); - zmax = std::max(tpcabox.MaxZ(), zmax); - - if(_debug) { - std::cout << "Using Cryostat " << tpc_id[0] << " TPC " << tpc_id[1] - << " ... X " << xmin << " => " << xmax - << " ... Y " << ymin << " => " << ymax - << " ... Z " << zmin << " => " << zmax - << std::endl; - } - } - - // range register - if(xrange.size()==1) { _xrange[0] = _xrange[1] = xrange[0]; } - if(yrange.size()==1) { _yrange[0] = _yrange[1] = yrange[0]; } - if(zrange.size()==1) { _zrange[0] = _zrange[1] = zrange[0]; } - if(xrange.size()==2) { _xrange[0] = xrange[0]; _xrange[1] = xrange[1]; } - if(yrange.size()==2) { _yrange[0] = yrange[0]; _yrange[1] = yrange[1]; } - if(zrange.size()==2) { _zrange[0] = zrange[0]; _zrange[1] = zrange[1]; } - - _xrange[0] = xmin + _xrange[0]; - _xrange[1] = xmax - _xrange[1]; - _yrange[0] = ymin + _yrange[0]; - _yrange[1] = ymax - _yrange[1]; - _zrange[0] = zmin + _zrange[0]; - _zrange[1] = zmax - _zrange[1]; - - // check - assert(_xrange[0] <= _xrange[1]); - assert(_yrange[0] <= _yrange[1]); - assert(_zrange[0] <= _zrange[1]); - - if(_debug>0) { - std::cout<<"Particle generation world boundaries..."< " << _xrange[1] << std::endl - <<"Y " << _yrange[0] << " => " << _yrange[1] << std::endl - <<"Z " << _zrange[0] << " => " << _zrange[1] << std::endl; - } - - - // register - //auto db = new TDatabasePDG; - auto db = TDatabasePDG::Instance(); - for(size_t idx=0; idxGetParticle(param.pdg[i])->Mass(); - - // sanity check - if(kerange[0]<0 || kerange[1]<0) - this->abort("You provided negative energy? Fuck off Mr. Trump."); - - // overall range check - if(param.kerange[0] > param.kerange[1]) this->abort("KE range has no phase space..."); - - if(_debug>0) { - std::cout << "Generating particle (PDG"; - for(auto const& pdg : param.pdg) std::cout << " " << pdg; - std::cout << ")" << std::endl - << (param.use_mom ? " KE range ....... " : " Mom range ....... ") - << param.kerange[0] << " => " << param.kerange[1] << " MeV" << std::endl - << std::endl; - } - - _param_v.push_back(param); - } -} - -void MultiPartVertex::beginRun(art::Run& run) -{ - // grab the geometry object to see what geometry we are using - art::ServiceHandle geo; - - std::unique_ptr runData(new sumdata::RunData(geo->DetectorName())); - - run.put(std::move(runData), art::fullRun()); - - return; -} - -std::vector MultiPartVertex::GenParticles() const { - - std::vector result; - std::vector gen_count_v(_param_v.size(),0); - - int num_part = (int)(fFlatRandom->fire(_multi_min,_multi_max+1-1.e-10)); - - // generate min multiplicity first - std::vector weight_v(_param_v.size(),0); - for(size_t idx=0; idx<_param_v.size(); ++idx) { - weight_v[idx] = _param_v[idx].weight; - for(size_t ctr=0; ctr<_param_v[idx].multi[0]; ++ctr) { - result.push_back(idx); - gen_count_v[idx] += 1; - num_part -= 1; - } - if(gen_count_v[idx] >= _param_v[idx].multi[1]) - weight_v[idx] = 0.; - } - - assert(num_part >= 0); - - while(num_part) { - - double total_weight = 0; - for(auto const& v : weight_v) total_weight += v; - - double rval = 0; - rval = fFlatRandom->fire(0,total_weight); - - size_t idx = 0; - for(idx = 0; idx < weight_v.size(); ++idx) { - rval -= weight_v[idx]; - if(rval <=0.) break; - } - - // register to the output - result.push_back(idx); - - // if generation count exceeds max, set probability weight to be 0 - gen_count_v[idx] += 1; - if(gen_count_v[idx] >= _param_v[idx].multi[1]) - weight_v[idx] = 0.; - - --num_part; - } - return result; -} - - -void MultiPartVertex::GenPosition(double& x, double& y, double& z) { - - x = fFlatRandom->fire(_xrange[0],_xrange[1]); - y = fFlatRandom->fire(_yrange[0],_yrange[1]); - z = fFlatRandom->fire(_zrange[0],_zrange[1]); - - if(_debug>0) { - std::cout << "Generating a rain particle at (" - << x << "," << y << "," << z << ")" << std::endl; - } -} - -void MultiPartVertex::GenBoost(double& bx, double& by, double& bz) { - - double gbmag = 0.; // saves gamma * beta - if ( _gamma_beta_range[1] > _gamma_beta_range[0]) { - gbmag = fFlatRandom->fire(_gamma_beta_range[0],_gamma_beta_range[1]); - } - else { - gbmag = _gamma_beta_range[0]; - } - double ct = fFlatRandom->fire(-1.0,1.0); - double st = TMath::Sqrt(1.0 - ct*ct); - double phi = fFlatRandom->fire(0.0,2.0 * M_PI); - double bmag = gbmag / TMath::Sqrt(1.0 + gbmag*gbmag); - - bx = bmag * st * TMath::Cos(phi); - by = bmag * st * TMath::Sin(phi); - bz = bmag * ct; - - if(_debug>0) { - std::cout << "Generated boost parameter gamma beta = " << gbmag << ", cos(theta) = " << ct - << ", phi = " << phi << std::endl; - std::cout << "Generating a boost (" - << bx << "," << by << "," << bz << ")" << std::endl; - } -} - -/* -void MultiPartVertex::GenPosition(double& x, double& y, double& z) { - - auto const& tpc_id = _tpc_v.at((size_t)(fFlatRandom->fire(0,_tpc_v.size()))); - // Implementation of required member function here. - auto geop = lar::providerFrom(); - size_t cid = tpc_id[0]; - size_t tid = tpc_id[1]; - auto const& cryostat = geop->Cryostat(cid); - if(!cryostat.HasTPC(tid)) { - std::cerr<< "\033[93mTPC " << tid << " not found... in cryostat " << cid << "\033[00m" << std::endl; - throw std::exception(); - } - - auto const& tpc = cryostat.TPC(tid); - auto const& tpcabox = tpc.ActiveBoundingBox(); - double xmin = tpcabox.MinX() + _xrange[0]; - double xmax = tpcabox.MaxX() - _xrange[1]; - double ymin = tpcabox.MinY() + _yrange[0]; - double ymax = tpcabox.MaxY() - _yrange[1]; - double zmin = tpcabox.MinZ() + _zrange[0]; -double zmax = tpcabox.MaxZ() - _zrange[1]; -x = fFlatRandom->fire(xmin,xmax); -y = fFlatRandom->fire(ymin,ymax); -z = fFlatRandom->fire(zmin,zmax); - -} -*/ - -std::array MultiPartVertex::extractDirection() const { - double ct = fFlatRandom->fire(-1.0,1.0); - double st = TMath::Sqrt(1.0 - ct*ct); - double phi = fFlatRandom->fire(0.0,2.0*M_PI); - double px = st * TMath::Cos(phi); - double py = st * TMath::Sin(phi); - double pz = ct; - std::array result = { px, py, pz }; - return result; -} -/* -std::array MultiPartVertex::extractDirection() const { - double px, py, pz; - px = fNormalRandom->fire(0, 1); - py = fNormalRandom->fire(0, 1); - pz = fNormalRandom->fire(0, 1); - double p = std::hypot(px, py, pz); - px = px / p; - py = py / p; - pz = pz / p; - std::array result = { px, py, pz }; - return result; -} -*/ - -void MultiPartVertex::GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz) { - - double tot_energy = 0; - if(param.use_mom) - tot_energy = sqrt(cet::square(fFlatRandom->fire(param.kerange[0],param.kerange[1])) + cet::square(mass)); - else - tot_energy = fFlatRandom->fire(param.kerange[0],param.kerange[1]) + mass; - - double mom_mag = sqrt(cet::square(tot_energy) - cet::square(mass)); - - std::array p = extractDirection(); - px = p[0]; py = p[1]; pz = p[2]; - - if(_debug>1) - std::cout << " Direction : (" << px << "," << py << "," << pz << ")" << std::endl - << " Momentum : " << mom_mag << " [MeV/c]" << std::endl - << " Energy : " << tot_energy << " [MeV/c^2]" << std::endl; - px *= mom_mag; - py *= mom_mag; - pz *= mom_mag; - -} - -void MultiPartVertex::GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz, bool& same_range) { - - if (!same_range){ - GenMomentum(param, mass, px, py, pz); - } - else{ - double tot_energy = 0; - if(param.use_mom) - tot_energy = sqrt(cet::square(fFlatRandom->fire(param.kerange[0],param.kerange[1])) + cet::square(mass)); - else - tot_energy = fFlatRandom->fire(param.kerange[0],1) + mass; - - double mom_mag = sqrt(cet::square(tot_energy) - cet::square(mass)); - - std::array p = extractDirection(); - px = p[0]; py = p[1]; pz = p[2]; - - if(_debug>1) - std::cout << " Direction : (" << px << "," << py << "," << pz << ")" << std::endl - << " Momentum : " << mom_mag << " [MeV/c]" << std::endl - << " Energy : " << tot_energy << " [MeV/c^2]" << std::endl; - px *= mom_mag; - py *= mom_mag; - pz *= mom_mag; - } -} - - -void MultiPartVertex::GenMomentumSF(const double& sf, const double& m, const double& p, double& p_sf) -{ - p_sf = sqrt( sf*( 2*(sf-1)*cet::square(m)-2*(sf-1)*m*sqrt(cet::square(m)+cet::square(p))+sf*cet::square(p) ) )/p; -} - -void MultiPartVertex::produce(art::Event & e) -{ - if(_debug>0) std::cout << "Processing a new event..." << std::endl; - - std::unique_ptr< std::vector > mctArray(new std::vector); - - double g4_time = fFlatRandom->fire(_t0 - _t0_sigma/2., _t0 + _t0_sigma/2.); - - double x, y, z; - GenPosition(x,y,z); - TLorentzVector pos(x,y,z,g4_time); - - double bx,by,bz; - GenBoost(bx,by,bz); - - simb::MCTruth mct; - - mct.SetOrigin(simb::kBeamNeutrino); - - std::vector part_v; - - std::vector E_vec; - std::vector mass_vec; - std::vector pdg_vec; - std::vector px_vec; - std::vector py_vec; - std::vector pz_vec; - - auto const param_idx_v = GenParticles(); - if(_debug) - std::cout << "Event Vertex @ (" << x << "," << y << "," << z << ") ... " << param_idx_v.size() << " particles..." << std::endl; - - for(size_t idx=0; idxfire(0,param.pdg.size()-1.e-10)); - auto const& pdg = param.pdg[pdg_index]; - auto const& mass = param.mass[pdg_index]; - if(_debug) std::cout << " " << idx << "th instance PDG " << pdg << std::endl; - - if (_revert) { - GenMomentum(param,mass,px,py,pz); - } - else { - GenMomentum(param,mass,px,py,pz,same_range); - } - // moving boost - // - //TLorentzVector mom(px,py,pz,sqrt(cet::square(px)+cet::square(py)+cet::square(pz)+cet::square(mass))); - // - /* - if(_use_boost){ - mom.Boost(bx,by,bz); - px=mom.X(); - py=mom.Y(); - pz=mom.Z(); - }*/ - - E_vec.push_back(sqrt(cet::square(px)+cet::square(py)+cet::square(pz)+cet::square(mass))); - mass_vec.push_back(mass); - pdg_vec.push_back(pdg); - px_vec.push_back(px); - py_vec.push_back(py); - pz_vec.push_back(pz); - - } - - if(_revert){ - for(size_t idx=0; idx1.){ - - for(size_t idx=0; idxfire(0,1); - double total_KE_sf = gen_total_KE/total_KE; - if(_debug) std::cout << "gen_total_KE: "<< gen_total_KE << " , KE scale factor : " << total_KE_sf << std::endl; - - for(size_t idx=0; idxpush_back(mct); - - e.put(std::move(mctArray)); -} - -DEFINE_ART_MODULE(MultiPartVertex) diff --git a/sbncode/EventGenerator/MultiPart/gen_mpvmpr.fcl b/sbncode/EventGenerator/MultiPart/gen_mpvmpr.fcl index 2b046e5e0..6193d8937 100644 --- a/sbncode/EventGenerator/MultiPart/gen_mpvmpr.fcl +++ b/sbncode/EventGenerator/MultiPart/gen_mpvmpr.fcl @@ -63,8 +63,8 @@ physics.producers.generator.ZRange : [30,30] physics.producers.generator.TPCRange : [[0,0],[0,1],[0,2],[0,3]] #[1,0],[1,1],[1,2],[1,3]] physics.producers.generator.MultiMax : 7 #6 physics.producers.generator.GammaBetaRange : [0.0, 3.0] # [0.0,20.0] # Boost, GammaBeta = Gamma * Beta -physics.producers.generator.Revert: 0 #1 -physics.producers.generator.UseBoost : 0# 1 +physics.producers.generator.Revert: false #1 +physics.producers.generator.UseBoost : false# 1 physics.producers.generator.MultiMin : 2 physics.producers.generator.ParticleParameter.PDGCode : [[-11,11,-13,13], [111], [211,-211], [2212], [22]] physics.producers.generator.ParticleParameter.MinMulti : [ 0, 0, 0, 0, 0] diff --git a/sbncode/EventGenerator/WireModGen/CMakeLists.txt b/sbncode/EventGenerator/WireModGen/CMakeLists.txt new file mode 100644 index 000000000..dfd93844e --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/CMakeLists.txt @@ -0,0 +1,52 @@ +cet_build_plugin(WireModMuon art::EDProducer + REG_SOURCE CORSIKAGenSBN.cc WireModMuon_module.cc + LIBRARIES PRIVATE + larcore::Geometry_Geometry_service + larcorealg::Geometry + larcoreobj::SummaryData + nurandom::RandomUtils_NuRandomService_service + nusimdata::SimulationBase + art::Framework_Principal + art::Framework_Services_Registry + messagefacility::MF_MessageLogger + fhiclcpp::fhiclcpp + ifdh::ifdh + messagefacility::MF_MessageLogger + ROOT::Core + ROOT::EG + CLHEP::Random + SQLite::SQLite3 + lardata::DetectorInfoServices_DetectorPropertiesServiceStandard_service + lardata::DetectorInfoServices_DetectorClocksServiceStandard_service + lardata::DetectorClocksService +) + +cet_build_plugin(FilterPrimarySimChannel art::EDProducer + LIBRARIES PRIVATE + larsim::Simulation + art::Framework_Principal + art::Framework_Services_Registry + messagefacility::MF_MessageLogger + fhiclcpp::fhiclcpp + messagefacility::MF_MessageLogger +) + +cet_build_plugin(FilterPrimarySimEnergyDepo art::EDProducer + LIBRARIES PRIVATE + art::Framework_Services_Registry + larsim::Simulation + art::Framework_Principal + art::Framework_Services_Registry + messagefacility::MF_MessageLogger + fhiclcpp::fhiclcpp + messagefacility::MF_MessageLogger + larcore::Geometry_Geometry_service + larcorealg::Geometry + lardata::DetectorInfoServices_DetectorPropertiesServiceStandard_service + lardata::DetectorInfoServices_DetectorClocksServiceStandard_service + lardata::DetectorClocksService +) + +install_headers() +install_fhicl() +install_source() diff --git a/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.cc b/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.cc new file mode 100644 index 000000000..a9a4be372 --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.cc @@ -0,0 +1,603 @@ +//////////////////////////////////////////////////////////////////////// +/// \file CORSIKAGenSBN_module.cc +/// \brief Generator for cosmic-ray secondaries based on pre-generated CORSIKA shower databases. +/// Overrides the larsim CORSIKAGen module to allow to be inherited by a base class. +/// +/// \author gputnam@fnal.gov, taken from module by: Matthew.Bass@physics.ox.ac.uk +//////////////////////////////////////////////////////////////////////// + +#include "sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.h" + +namespace evgen { + + CORSIKAGenSBN::CORSIKAGenSBN(fhicl::ParameterSet const& p) + : EDProducer{p} + , fProjectToHeight(p.get("ProjectToHeight", 0.)) + , fShowerInputFiles(p.get>("ShowerInputFiles")) + , fShowerCopyType(p.get("ShowerCopyType", "IFDH")) + , fShowerFluxConstants(p.get>("ShowerFluxConstants")) + , fSampleTime(p.get("SampleTime", 0.)) + , fToffset(p.get("TimeOffset", 0.)) + , fBuffBox(p.get>("BufferBox", {0.0, 0.0, 0.0, 0.0, 0.0, 0.0})) + , fShowerAreaExtension(p.get("ShowerAreaExtension", 0.)) + , fRandomXZShift(p.get("RandomXZShift", 0.)) + , fGenEngine(art::ServiceHandle()->registerAndSeedEngine( + createEngine(0, "HepJamesRandom", "gen"), + "HepJamesRandom", + "gen", + p, + {"Seed", "SeedGenerator"})) + , fPoisEngine(art::ServiceHandle()->registerAndSeedEngine( + createEngine(0, "HepJamesRandom", "pois"), + "HepJamesRandom", + "pois", + p, + "SeedPoisson")) + { + if (fShowerInputFiles.size() != fShowerFluxConstants.size() || fShowerInputFiles.size() == 0 || + fShowerFluxConstants.size() == 0) + throw cet::exception("CORSIKAGenSBN") + << "ShowerInputFiles and ShowerFluxConstants have different or invalid sizes!" + << "\n"; + fShowerInputs = fShowerInputFiles.size(); + + if (fSampleTime == 0.) throw cet::exception("CORSIKAGenSBN") << "SampleTime not set!"; + + if (fProjectToHeight == 0.) mf::LogInfo("CORSIKAGenSBN") << "Using 0. for fProjectToHeight!"; + // create a default random engine; obtain the random seed from NuRandomService, + // unless overridden in configuration with key "Seed" + + this->openDBs(p.get("module_label")); + this->populateNShowers(); + this->populateTOffset(); + + produces>(); + produces(); + } + + CORSIKAGenSBN::~CORSIKAGenSBN() + { + for (int i = 0; i < fShowerInputs; i++) { + sqlite3_close(fdb[i]); + } + //cleanup temp files + fIFDH->cleanup(); + } + + void CORSIKAGenSBN::ProjectToBoxEdge(const double xyz[], + const double indxyz[], + const double xlo, + const double xhi, + const double ylo, + const double yhi, + const double zlo, + const double zhi, + double xyzout[]) + { + + //we want to project backwards, so take mirror of momentum + const double dxyz[3] = {-indxyz[0], -indxyz[1], -indxyz[2]}; + + // Compute the distances to the x/y/z walls + double dx = 99.E99; + double dy = 99.E99; + double dz = 99.E99; + if (dxyz[0] > 0.0) { dx = (xhi - xyz[0]) / dxyz[0]; } + else if (dxyz[0] < 0.0) { + dx = (xlo - xyz[0]) / dxyz[0]; + } + if (dxyz[1] > 0.0) { dy = (yhi - xyz[1]) / dxyz[1]; } + else if (dxyz[1] < 0.0) { + dy = (ylo - xyz[1]) / dxyz[1]; + } + if (dxyz[2] > 0.0) { dz = (zhi - xyz[2]) / dxyz[2]; } + else if (dxyz[2] < 0.0) { + dz = (zlo - xyz[2]) / dxyz[2]; + } + + // Choose the shortest distance + double d = 0.0; + if (dx < dy && dx < dz) + d = dx; + else if (dy < dz && dy < dx) + d = dy; + else if (dz < dx && dz < dy) + d = dz; + + // Make the step + for (int i = 0; i < 3; ++i) { + xyzout[i] = xyz[i] + dxyz[i] * d; + } + } + + void CORSIKAGenSBN::openDBs(std::string const& module_label) + { + //choose files based on fShowerInputFiles, copy them with ifdh, open them + // for c2: statement is unused + //sqlite3_stmt *statement; + CLHEP::RandFlat flat(fGenEngine); + + //setup ifdh object + if (!fIFDH) fIFDH = new ifdh_ns::ifdh; + const char* ifdh_debug_env = std::getenv("IFDH_DEBUG_LEVEL"); + if (ifdh_debug_env) { + mf::LogInfo("CORSIKAGenSBN") << "IFDH_DEBUG_LEVEL: " << ifdh_debug_env << "\n"; + fIFDH->set_debug(ifdh_debug_env); + } + + //get ifdh path for each file in fShowerInputFiles, put into selectedflist + //if 1 file returned, use that file + //if >1 file returned, randomly select one file + //if 0 returned, make exeption for missing files + std::vector> selectedflist; + for (int i = 0; i < fShowerInputs; i++) { + if (fShowerInputFiles[i].find("*") == std::string::npos) { + //if there are no wildcards, don't call findMatchingFiles + selectedflist.push_back(std::make_pair(fShowerInputFiles[i], 0)); + mf::LogInfo("CorsikaGen") << "Selected" << selectedflist.back().first << "\n"; + } + else { + //use findMatchingFiles + //default to IFDH approach when wildcards in path + std::vector> flist; + std::string path(gSystem->DirName(fShowerInputFiles[i].c_str())); + std::string pattern(gSystem->BaseName(fShowerInputFiles[i].c_str())); + flist = fIFDH->findMatchingFiles(path, pattern); + unsigned int selIndex = -1; + if (flist.size() == 1) { //0th element is the search path:pattern + selIndex = 0; + } + else if (flist.size() > 1) { + selIndex = (unsigned int)(flat() * (flist.size() - 1) + + 0.5); //rnd with rounding, dont allow picking the 0th element + } + else { + throw cet::exception("CORSIKAGenSBN") + << "No files returned for path:pattern: " << path << ":" << pattern << std::endl; + } + selectedflist.push_back(flist[selIndex]); + mf::LogInfo("CorsikaGen") << "For " << fShowerInputFiles[i] << ":" << pattern << "\nFound " + << flist.size() << " candidate files" + << "\nChoosing file number " << selIndex << "\n" + << "\nSelected " << selectedflist.back().first << "\n"; + } + } + + //do the fetching, store local filepaths in locallist + std::vector locallist; + for (unsigned int i = 0; i < selectedflist.size(); i++) { + mf::LogInfo("CorsikaGen") << "Fetching: " << selectedflist[i].first << " " + << selectedflist[i].second << "\n"; + if (fShowerCopyType == "IFDH") { + std::string fetchedfile(fIFDH->fetchInput(selectedflist[i].first)); + MF_LOG_DEBUG("CorsikaGen") << "Fetched; local path: " << fetchedfile << "; copy type: IFDH"; + locallist.push_back(fetchedfile); + } + else if (fShowerCopyType == "DIRECT") { + std::string fetchedfile(selectedflist[i].first); + MF_LOG_DEBUG("CorsikaGen") + << "Fetched; local path: " << fetchedfile << "; copy type: DIRECT"; + locallist.push_back(fetchedfile); + } + else + throw cet::exception("CORSIKAGenSBN") + << "Error copying shower db: ShowerCopyType must be \"IFDH\" or \"DIRECT\"\n"; + } + + //open the files in fShowerInputFilesLocalPaths with sqlite3 + for (unsigned int i = 0; i < locallist.size(); i++) { + //prepare and execute statement to attach db file + int res = sqlite3_open(locallist[i].c_str(), &fdb[i]); + if (res != SQLITE_OK) + throw cet::exception("CORSIKAGenSBN") + << "Error opening db: (" << locallist[i].c_str() << ") (" << res + << "): " << sqlite3_errmsg(fdb[i]) << "; memory used:<<" << sqlite3_memory_used() << "/" + << sqlite3_memory_highwater(0) << "\n"; + else + mf::LogInfo("CORSIKAGenSBN") << "Attached db " << locallist[i] << "\n"; + } + } + + double CORSIKAGenSBN::wrapvar(const double var, const double low, const double high) + { + //wrap variable so that it's always between low and high + return (var - (high - low) * floor(var / (high - low))) + low; + } + + double CORSIKAGenSBN::wrapvarBoxNo(const double var, const double low, const double high, int& boxno) + { + //wrap variable so that it's always between low and high + boxno = int(floor(var / (high - low))); + return (var - (high - low) * floor(var / (high - low))) + low; + } + + void CORSIKAGenSBN::populateTOffset() + { + //populate TOffset_corsika by finding minimum ParticleTime from db file + + sqlite3_stmt* statement; + const std::string kStatement("select min(t) from particles"); + double t = 0.; + + for (int i = 0; i < fShowerInputs; i++) { + //build and do query to get run min(t) from each db + if (sqlite3_prepare(fdb[i], kStatement.c_str(), -1, &statement, 0) == SQLITE_OK) { + int res = 0; + res = sqlite3_step(statement); + if (res == SQLITE_ROW) { + t = sqlite3_column_double(statement, 0); + mf::LogInfo("CORSIKAGenSBN") + << "For showers input " << i << " found particles.min(t)=" << t << "\n"; + if (i == 0 || t < fToffset_corsika) fToffset_corsika = t; + } + else { + throw cet::exception("CORSIKAGenSBN") + << "Unexpected sqlite3_step return value: (" << res << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + } + else { + throw cet::exception("CORSIKAGenSBN") << "Error preparing statement: (" << kStatement << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + } + + mf::LogInfo("CORSIKAGenSBN") << "Found corsika timeoffset [ns]: " << fToffset_corsika << "\n"; + } + + void CORSIKAGenSBN::populateNShowers() + { + //populate vector of the number of showers per event based on: + //AREA the showers are being distributed over + //TIME of the event (fSampleTime) + //flux constants that determine the overall normalizations (fShowerFluxConstants) + //Energy range over which the sample was generated (ERANGE_*) + //power spectrum over which the sample was generated (ESLOPE) + + //compute shower area based on the maximal x,z dimensions of cryostat boundaries + fShowerAreaExtension + art::ServiceHandle geom; + for (auto const& cryostat : geom->Iterate()) { + double bounds[6] = {0.}; + cryostat.Boundaries(bounds); + for (unsigned int bnd = 0; bnd < 6; bnd++) { + mf::LogVerbatim("CORSIKAGenSBN") << "Cryo Boundary: " << bnd << "=" << bounds[bnd] + << " ( + Buffer=" << fBuffBox[bnd] << ")\n"; + if (fabs(bounds[bnd]) > fabs(fShowerBounds[bnd])) { fShowerBounds[bnd] = bounds[bnd]; } + } + } + //add on fShowerAreaExtension without being clever + fShowerBounds[0] = fShowerBounds[0] - fShowerAreaExtension; + fShowerBounds[1] = fShowerBounds[1] + fShowerAreaExtension; + fShowerBounds[4] = fShowerBounds[4] - fShowerAreaExtension; + fShowerBounds[5] = fShowerBounds[5] + fShowerAreaExtension; + + double showersArea = (fShowerBounds[1] / 100 - fShowerBounds[0] / 100) * + (fShowerBounds[5] / 100 - fShowerBounds[4] / 100); + + mf::LogInfo("CORSIKAGenSBN") << "Area extended by : " << fShowerAreaExtension + << "\nShowers to be distributed betweeen: x=" << fShowerBounds[0] + << "," << fShowerBounds[1] << " & z=" << fShowerBounds[4] << "," + << fShowerBounds[5] + << "\nShowers to be distributed with random XZ shift: " + << fRandomXZShift << " cm" + << "\nShowers to be distributed over area: " << showersArea << " m^2" + << "\nShowers to be distributed over time: " << fSampleTime << " s" + << "\nShowers to be distributed with time offset: " << fToffset + << " s" + << "\nShowers to be distributed at y: " << fShowerBounds[3] << " cm"; + + //db variables + sqlite3_stmt* statement; + const std::string kStatement("select erange_high,erange_low,eslope,nshow from input"); + double upperLimitOfEnergyRange = 0., lowerLimitOfEnergyRange = 0., energySlope = 0., + oneMinusGamma = 0., EiToOneMinusGamma = 0., EfToOneMinusGamma = 0.; + + for (int i = 0; i < fShowerInputs; i++) { + //build and do query to get run info from databases + // double thisrnd=flat();//need a new random number for each query + if (sqlite3_prepare(fdb[i], kStatement.c_str(), -1, &statement, 0) == SQLITE_OK) { + int res = 0; + res = sqlite3_step(statement); + if (res == SQLITE_ROW) { + upperLimitOfEnergyRange = sqlite3_column_double(statement, 0); + lowerLimitOfEnergyRange = sqlite3_column_double(statement, 1); + energySlope = sqlite3_column_double(statement, 2); + fMaxShowers.push_back(sqlite3_column_int(statement, 3)); + oneMinusGamma = 1 + energySlope; + EiToOneMinusGamma = pow(lowerLimitOfEnergyRange, oneMinusGamma); + EfToOneMinusGamma = pow(upperLimitOfEnergyRange, oneMinusGamma); + mf::LogVerbatim("CORSIKAGenSBN") + << "For showers input " << i << " found e_hi=" << upperLimitOfEnergyRange + << ", e_lo=" << lowerLimitOfEnergyRange << ", slope=" << energySlope + << ", k=" << fShowerFluxConstants[i] << "\n"; + } + else { + throw cet::exception("CORSIKAGenSBN") + << "Unexpected sqlite3_step return value: (" << res << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + } + else { + throw cet::exception("CORSIKAGenSBN") << "Error preparing statement: (" << kStatement << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + + //this is computed, how? + double NShowers = (M_PI * showersArea * fShowerFluxConstants[i] * + (EfToOneMinusGamma - EiToOneMinusGamma) / oneMinusGamma) * + fSampleTime; + fNShowersPerEvent.push_back(NShowers); + mf::LogVerbatim("CORSIKAGenSBN") + << "For showers input " << i << " the number of showers per event is " << (int)NShowers + << "\n"; + } + } + + void CORSIKAGenSBN::GetSample(simb::MCTruth& mctruth) + { + //for each input, randomly pull fNShowersPerEvent[i] showers from the Particles table + //and randomly place them in time (between -fSampleTime/2 and fSampleTime/2) + //wrap their positions based on the size of the area under consideration + //based on http://nusoft.fnal.gov/larsoft/doxsvn/html/CRYHelper_8cxx_source.html (Sample) + + //query from sqlite db with select * from particles where shower in (select id from showers ORDER BY substr(id*0.51123124141,length(id)+2) limit 100000) ORDER BY substr(shower*0.51123124141,length(shower)+2); + //where 0.51123124141 is a random seed to allow randomly selecting rows and should be randomly generated for each query + //the inner order by is to select randomly from the possible shower id's + //the outer order by is to make sure the shower numbers are ordered randomly (without this, the showers always come out ordered by shower number + //and 100000 is the number of showers to be selected at random and needs to be less than the number of showers in the showers table + + //TDatabasePDG is for looking up particle masses + static TDatabasePDG* pdgt = TDatabasePDG::Instance(); + + //db variables + sqlite3_stmt* statement; + const TString kStatement( + "select shower,pdg,px,py,pz,x,z,t,e from particles where shower in (select id from showers " + "ORDER BY substr(id*%f,length(id)+2) limit %d) ORDER BY substr(shower*%f,length(shower)+2)"); + + CLHEP::RandFlat flat(fGenEngine); + CLHEP::RandPoissonQ randpois(fPoisEngine); + + // get geometry and figure where to project particles to, based on CRYHelper + art::ServiceHandle geom; + double x1, x2; + double y1, y2; + double z1, z2; + geom->WorldBox(&x1, &x2, &y1, &y2, &z1, &z2); + + // make the world box slightly smaller so that the projection to + // the edge avoids possible rounding errors later on with Geant4 + double fBoxDelta = 1.e-5; + x1 += fBoxDelta; + x2 -= fBoxDelta; + y1 += fBoxDelta; + y2 = fProjectToHeight; + z1 += fBoxDelta; + z2 -= fBoxDelta; + + //populate mctruth + int ntotalCtr = 0; //count number of particles added to mctruth + int lastShower = + 0; //keep track of last shower id so that t can be randomized on every new shower + int nShowerCntr = 0; //keep track of how many showers are left to be added to mctruth + int nShowerQry = 0; //number of showers to query from db + int shower, pdg; + double px, py, pz, x, z, tParticleTime, etot, + showerTime = 0., showerTimex = 0., showerTimez = 0., showerXOffset = 0., showerZOffset = 0., + t; + for (int i = 0; i < fShowerInputs; i++) { + nShowerCntr = randpois.fire(fNShowersPerEvent[i]); + mf::LogInfo("CORSIKAGEN") << " Shower input " << i << " with mean " << fNShowersPerEvent[i] + << " generating " << nShowerCntr; + + while (nShowerCntr > 0) { + //how many showers should we query? + if (nShowerCntr > fMaxShowers[i]) { + nShowerQry = fMaxShowers[i]; //take the group size + } + else { + nShowerQry = nShowerCntr; //take the rest that are needed + } + //build and do query to get nshowers + double thisrnd = flat(); //need a new random number for each query + TString kthisStatement = TString::Format(kStatement.Data(), thisrnd, nShowerQry, thisrnd); + MF_LOG_DEBUG("CORSIKAGenSBN") << "Executing: " << kthisStatement; + if (sqlite3_prepare(fdb[i], kthisStatement.Data(), -1, &statement, 0) == SQLITE_OK) { + int res = 0; + //loop over database rows, pushing particles into mctruth object + while (1) { + res = sqlite3_step(statement); + if (res == SQLITE_ROW) { + /* + * Memo columns: + * [0] shower + * [1] particle ID (PDG) + * [2] momentum: x component [GeV/c] + * [3] momentum: y component [GeV/c] + * [4] momentum: z component [GeV/c] + * [5] position: x component [cm] + * [6] position: z component [cm] + * [7] time [ns] + * [8] energy [GeV] + */ + shower = sqlite3_column_int(statement, 0); + if (shower != lastShower) { + //each new shower gets its own random time and position offsets + showerTime = 1e9 * (flat() * fSampleTime); //converting from s to ns + showerTimex = 1e9 * (flat() * fSampleTime); //converting from s to ns + showerTimez = 1e9 * (flat() * fSampleTime); //converting from s to ns + //and a random offset in both z and x controlled by the fRandomXZShift parameter + showerXOffset = flat() * fRandomXZShift - (fRandomXZShift / 2); + showerZOffset = flat() * fRandomXZShift - (fRandomXZShift / 2); + } + pdg = sqlite3_column_int(statement, 1); + //get mass for this particle + double m = 0.; // in GeV + TParticlePDG* pdgp = pdgt->GetParticle(pdg); + if (pdgp) m = pdgp->Mass(); + + //Note: position/momentum in db have north=-x and west=+z, rotate so that +z is north and +x is west + //get momentum components + px = sqlite3_column_double(statement, 4); //uboone x=Particlez + py = sqlite3_column_double(statement, 3); + pz = -sqlite3_column_double(statement, 2); //uboone z=-Particlex + etot = sqlite3_column_double(statement, 8); + + //get/calculate position components + int boxnoX = 0, boxnoZ = 0; + x = wrapvarBoxNo(sqlite3_column_double(statement, 6) + showerXOffset, + fShowerBounds[0], + fShowerBounds[1], + boxnoX); + z = wrapvarBoxNo(-sqlite3_column_double(statement, 5) + showerZOffset, + fShowerBounds[4], + fShowerBounds[5], + boxnoZ); + tParticleTime = sqlite3_column_double( + statement, 7); //time offset, includes propagation time from top of atmosphere + //actual particle time is particle surface arrival time + //+ shower start time + //+ global offset (fcl parameter, in s) + //- propagation time through atmosphere + //+ boxNo{X,Z} time offset to make grid boxes have different shower times + t = tParticleTime + showerTime + (1e9 * fToffset) - fToffset_corsika + + showerTimex * boxnoX + showerTimez * boxnoZ; + //wrap surface arrival so that it's in the desired time window + t = wrapvar(t, (1e9 * fToffset), 1e9 * (fToffset + fSampleTime)); + + simb::MCParticle p(ntotalCtr, pdg, "primary", -200, m, 1); + + //project back to wordvol/fProjectToHeight + /* + * This back propagation goes from a point on the upper surface of + * the cryostat back to the edge of the world, except that that + * world is cut short by `fProjectToHeight` (`y2`) ceiling. + * The projection will most often lie on that ceiling, but it may + * end up instead on one of the side edges of the world, or even + * outside it. + */ + double xyzo[3]; + double x0[3] = {x, fShowerBounds[3], z}; + double dx[3] = {px, py, pz}; + this->ProjectToBoxEdge(x0, dx, x1, x2, y1, y2, z1, z2, xyzo); + + TLorentzVector pos( + xyzo[0], xyzo[1], xyzo[2], t); // time needs to be in ns to match GENIE, etc + TLorentzVector mom(px, py, pz, etot); + p.AddTrajectoryPoint(pos, mom); + mctruth.Add(p); + ntotalCtr++; + lastShower = shower; + } + else if (res == SQLITE_DONE) { + break; + } + else { + throw cet::exception("CORSIKAGenSBN") + << "Unexpected sqlite3_step return value: (" << res << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + } + } + else { + throw cet::exception("CORSIKAGenSBN") + << "Error preparing statement: (" << kthisStatement << "); " + << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n"; + } + nShowerCntr = nShowerCntr - nShowerQry; + } + } + } + + void CORSIKAGenSBN::beginRun(art::Run& run) + { + art::ServiceHandle geo; + run.put(std::make_unique(geo->DetectorName()), art::fullRun()); + } + + void CORSIKAGenSBN::produce(art::Event& evt) + { + std::unique_ptr> truthcol(new std::vector); + + art::ServiceHandle geom; + + simb::MCTruth truth; + truth.SetOrigin(simb::kCosmicRay); + + simb::MCTruth pretruth; + GetSample(pretruth); + mf::LogInfo("CORSIKAGenSBN") << "GetSample number of particles returned: " << pretruth.NParticles() + << "\n"; + // loop over particles in the truth object + for (int i = 0; i < pretruth.NParticles(); ++i) { + simb::MCParticle particle = pretruth.GetParticle(i); + const TLorentzVector& v4 = particle.Position(); + const TLorentzVector& p4 = particle.Momentum(); + double x0[3] = {v4.X(), v4.Y(), v4.Z()}; + double dx[3] = {p4.Px(), p4.Py(), p4.Pz()}; + + // now check if the particle goes through any cryostat in the detector + // if so, add it to the truth object. + for (auto const& cryostat : geom->Iterate()) { + double bounds[6] = {0.}; + cryostat.Boundaries(bounds); + + //add a buffer box around the cryostat bounds to increase the acceptance and account for scattering + //By default, the buffer box has zero size + for (unsigned int cb = 0; cb < 6; cb++) + bounds[cb] = bounds[cb] + fBuffBox[cb]; + + //calculate the intersection point with each cryostat surface + bool intersects_cryo = false; + for (int bnd = 0; bnd != 6; ++bnd) { + if (bnd < 2) { + double p2[3] = {bounds[bnd], + x0[1] + (dx[1] / dx[0]) * (bounds[bnd] - x0[0]), + x0[2] + (dx[2] / dx[0]) * (bounds[bnd] - x0[0])}; + if (p2[1] >= bounds[2] && p2[1] <= bounds[3] && p2[2] >= bounds[4] && + p2[2] <= bounds[5]) { + intersects_cryo = true; + break; + } + } + else if (bnd >= 2 && bnd < 4) { + double p2[3] = {x0[0] + (dx[0] / dx[1]) * (bounds[bnd] - x0[1]), + bounds[bnd], + x0[2] + (dx[2] / dx[1]) * (bounds[bnd] - x0[1])}; + if (p2[0] >= bounds[0] && p2[0] <= bounds[1] && p2[2] >= bounds[4] && + p2[2] <= bounds[5]) { + intersects_cryo = true; + break; + } + } + else if (bnd >= 4) { + double p2[3] = {x0[0] + (dx[0] / dx[2]) * (bounds[bnd] - x0[2]), + x0[1] + (dx[1] / dx[2]) * (bounds[bnd] - x0[2]), + bounds[bnd]}; + if (p2[0] >= bounds[0] && p2[0] <= bounds[1] && p2[1] >= bounds[2] && + p2[1] <= bounds[3]) { + intersects_cryo = true; + break; + } + } + } + + if (intersects_cryo) { + truth.Add(particle); + break; //leave loop over cryostats to avoid adding particle multiple times + } // end if particle goes into a cryostat + } // end loop over cryostats in the detector + + } // loop on particles + + mf::LogInfo("CORSIKAGenSBN") + << "Number of particles from getsample crossing cryostat + bounding box: " + << truth.NParticles() << "\n"; + + truthcol->push_back(truth); + evt.put(std::move(truthcol)); + + return; + } // end produce + +} // end namespace diff --git a/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.h b/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.h new file mode 100644 index 000000000..e70d45c53 --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.h @@ -0,0 +1,299 @@ +//////////////////////////////////////////////////////////////////////// +/// \file CORSIKAGenSBN.h +/// \brief Class definition for cosmic-ray generator. +/// +/// \author gputnam@fnal.gov, taken from module by: Matthew.Bass@physics.ox.ac.uk +//////////////////////////////////////////////////////////////////////// +// +// ROOT includes +#include "TDatabasePDG.h" +#include "TString.h" +#include "TSystem.h" //need BaseName and DirName + +// Framework includes +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +// art extensions +#include "nurandom/RandomUtils/NuRandomService.h" + +// larsoft includes +#include "larcore/Geometry/Geometry.h" +#include "larcoreobj/SummaryData/RunData.h" +#include "nusimdata/SimulationBase/MCParticle.h" +#include "nusimdata/SimulationBase/MCTruth.h" + +#include "CLHEP/Random/RandFlat.h" +#include "CLHEP/Random/RandPoissonQ.h" +#include "ifdh.h" //to handle flux files +#include + +namespace evgen { + + /** + * @brief LArSoft interface to CORSIKA event generator. + * + * @note A presentation on this module by the original author is archived at: + * https://indico.fnal.gov/event/10893/contribution/3/material/slides + * + * In CORSIKA jargon, a "shower" is the cascade of particles resulting from + * a primary cosmic ray interaction. + * This module creates a single `simb::MCTruth` object (stored as data product + * into a `std::vector` with a single entry) containing all + * the particles from cosmic ray showers crossing a _surface_ above the + * detector. + * + * The generation procedure consists of selecting showers from a database of + * *pregenerated* events, and then to adapt them to the parameters requested + * in the module configuration. Pregenerated showers are "observed" at a + * altitude set in CORSIKA configuration. + * + * Databases need to be stored as files in SQLite3 format. Multiple file + * sources can be specified (`ShowerInputFiles` configuration parameter). + * From each source, one database file is selected and copied locally via + * IFDH. + * From each source, showers are extracted proportionally to the relative flux + * specified in the configuration (specified in `ShowerFluxConstants`, + * see @ref CORSIKAGenSBN_Normalization "normalization" below). + * The actual number of showers per event and per source is extracted + * according to a Poisson distribution around the predicted average number of + * primary cosmic rays for that source. + * + * + * Flux normalization + * ------------------- + * + * @anchor CORSIKAGenSBN_Normalization + * + * CORSIKA generates showers from each specific cosmic ray type @f$ A @f$ + * (e.g. iron, proton, etc.) according to a power law distribution + * @f$ \Phi_{A}(E) \propto E^{-\gamma_{A}} @f$ of the primary + * particle energy @f$ E @f$ [GeV]. When sampling pregenerated events, we + * bypass the normalization imposed by CORSIKA and gain complete + * control on it. + * + * Within CORSIKAGenSBN, for each source (usually each on a different primary + * cosmic ray type, e.g. iron, proton, etc.), the average number of generated + * showers is + * @f$ n_{A} = \pi S T k_{A} \int E^{-\gamma_{A}} dE @f$ with @f$ S @f$ the + * area of the surface the flux passes across, @f$ T @f$ the exposure time, + * the integral defined over the full energy range of the pregenerated showers + * in the source, and @f$ k_{A} @f$ a factor specified in the configuration + * (`ShowerFluxConstants` parameters). + * This is the flux of primary cosmic rays, not of the observed particles + * from their showers. Note that it depends on an area and a time interval, + * but it is uniform with respect to translations and constant in time. + * + * As explained @ref CORSIKAGenSBN_Coverage "below", we consider only the + * secondary particles that cross an "observation" surface. + * After cosmic ray primary particles cross the flux surface (@f$ S_{\Phi} @f$ + * above), they develop into showers of particles that spread across large + * areas. Limiting ourself to the observation of particles on a small surface + * @f$ S_{o} @f$ has two effects. We lose the part of the showers that misses + * that surface @f$ S_{o} @f$. Also, considering a span of time with + * multiple showers, we miss particles from other hypothetical showers + * whose primaries crossed outside the generation surface @f$ S_{\Phi} @f$ + * whose shower development would leak into @f$ S_{o} @f$: we did not simulate + * those showers at all! + * In terms of total flux of observed particles, under the assumption that the + * flux is uniform in space, choosing @f$ S_{\Phi} @f$ the same size as + * @f$ S_{o} @f$ makes the two effects get the same size: just as many + * particles from primaries from @f$ S_{\Phi} @f$ leak out of @f$ S_{o} @f$, + * as many particles from primaries from outside @f$ S_{\Phi} @f$ sneak in + * @f$ S_{o} @f$. + * In that case, counting _all_ the particles from the primaries crossing a + * surface @f$ S_{\Phi} @f$ of area _S_ regardless of where they land + * yields the right amount of cosmic ray secondary particles across an + * observation surface @f$ S_{o} @f$ also of area _S_. Practically, + * the particles landing outside @f$ S_{o} @f$ need to be recovered to + * preserve the correct normalization, which is described in the + * @ref CORSIKAGenSBN_Coverage "next section". + * + * + * Surface coverage, position and timing + * -------------------------------------- + * + * @anchor CORSIKAGenSBN_Coverage + * + * The surface we detect the particles through (let's call it @f$ S_{d} @f$) + * is defined by the smallest _rectangle_ including all cryostats in the + * detector, and located at the height of the ceiling of the tallest cryostat. + * This surface can be increased by specifying a positive value for + * `ShowerAreaExtension` configuration parameter, in which case each side of + * the rectangle will be extended by that amount. + * + * Showers are extracted one by one from the pregenerated samples and treated + * independently. + * Ideally, the detection surface @f$ S_{d} @f$ would be at the same exact + * altitude as the observation surface set in CORSIKA (called @f$ S_{o} @f$ + * above). In practice, we go the other way around, with the assumption that + * the shower observed at @f$ S_{d} @f$ would be very close to the one we + * actually generated at @f$ S_{o} @f$, and teleport the generated particles + * on @f$ S_{d} @f$. Since the cryostats may be just meters from the earth + * surface @f$ S_{o} @f$ lies on, this is an acceptable approximation. + * + * All the particles of one shower are compelled within surface @f$ S_{d} @f$ + * as a first step. As explained when describing the + * @anchor CORSIKAGenSBN_Normalization "normalization", we need to keep all the + * shower particles, one way or the other. + * So, particles of the shower that fell out of @f$ S_{d} @f$ are repackaged + * into other showers and translated back in. This is equivalent to assume the + * primary cosmic rays originating such shower would happen outside + * our generation volume (@f$ S_{\Phi} @f$), and their shower would then spill + * into @f$ S_{d} @f$. Since these repackaged showers are in principle + * independent and uncorrelated, they are assigned a random time different + * than the main shower, leveraging the assumption of constantness of the + * flux. + * + * As for the azimuth, this module uses an approximation by setting north + * direction to match the _z_ axis of LArSoft geometry (typically assumed + * to be the direction of the beam particle). + * + * The particles so manipulated are then back-propagated from the observation + * surface to an absolute height defined by `ProjectToHeight` (although for + * particular combination of position and direction, the particles might be + * propagated back to the edge of the world, or even outside it). + * + * As a final filter, only the particles whose straight projections cross any + * of the cryostats (with some buffer volume around, defined by `BufferBox`) + * are stored, while the other ones are discarded. Note that the actual + * interactions that particles from the generation surface undergo may deviate + * them enough to miss the cryostats anyway, and conversely particles that + * have been filtered out because shooting off the cryostats might have been + * subsequently deviated to actually cross them. This effect is not corrected + * for at this time. + * + * The time of the showers is uniformly distributed within the configured + * time interval, defined by `SampleTime` starting from `TimeOffset`. + * + * + * Configuration parameters + * ========================= + * + * * `ShowerInputFiles` (list of paths; mandatory): a list of file paths to + * pregenerated CORSIKA shower files. Each entry can be a single file or + * use wildcards (`*`) to specify a set of files to choose among. + * Paths and wildcards are processed by IFDH. + * * `ShowerFluxConstants` (list of real numbers; mandatory): for each entry + * @f$ A @f$ in `ShowerInputFiles`, specify the normalization factor + * @f$ K_{A} @f$ of their distribution [@f$ m^{-2}s^{-1} @f$] + * * `ProjectToHeight` (real, default: `0`): the generated particles will + * appear to come from this height [cm] + * * `TimeOffset` (real; default: `0`): start time of the exposure window [s], + * relative to the + * @ref DetectorClocksSimulationTime "simulation time start" + * * `SampleTime` (real; mandatory): duration of the simulated exposure to + * cosmic rays [s] + * * `ShowerAreaExtension` (real; default: `0`): + * extend the size of the observation surface of shower particles (_S_) + * by this much [cm]; e.g. 1000 will extend 10 m on each side + * * `RandomXZShift` (real; default: `0`): the original position of each + * shower is randomly shifted within a square with this length as side + * [cm] + * * `BufferBox` (list of six lengths, all `0` by default): + * extension to the volume of each cryostat for the purpose of filtering + * out the particles which do not cross the detector; each cryostat volume + * is independently extended by the same amount, specified here as + * *shifts* to lower _x_, higher _x_, lower _y_, higher _y_, lower _z_ + * and higher _z_, in that order [cm] (note that to extend e.g. the + * negative _x_ side by 5 meters the parameter value should be -500) + * * `SeedGenerator` (integer): force random number generator for event + * generation to the specified value + * * `SeedPoisson` (integer): force random number generator for number of + * showers to the specified value + * * `Seed`: alias for `SeedGenerator` + * + * + * Random engines + * --------------- + * + * Currently two random engines are used: + * * a generator engine (driven by `SeedGenerator`), of general use + * * a "Poisson" engine (driven by `SeedPoisson`), only used to determine the + * number of showers to be selected on each event + * + */ + class CORSIKAGenSBN : public art::EDProducer { + public: + explicit CORSIKAGenSBN(fhicl::ParameterSet const& pset); + virtual ~CORSIKAGenSBN(); + + void produce(art::Event& evt); + void beginRun(art::Run& run); + + protected: + void openDBs(std::string const& module_label); + void populateNShowers(); + void populateTOffset(); + void GetSample(simb::MCTruth&); + double wrapvar(const double var, const double low, const double high); + double wrapvarBoxNo(const double var, const double low, const double high, int& boxno); + /** + * @brief Propagates a point back to the surface of a box. + * @param xyz coordinates of the point to be propagated (`{ x, y, z }`) + * @param dxyz direction of the point (`{ dx, dy, dz }`) + * @param xlo lower _x_ coordinate of the target box + * @param xhi upper _x_ coordinate of the target box + * @param ylo lower _y_ coordinate of the target box + * @param yhi upper _y_ coordinate of the target box + * @param zlo lower _z_ coordinate of the target box + * @param zhi upper _z_ coordinate of the target box + * @param xyzout _(output, room for at least 3 numbers)_ propagated point + * + * The point `xyz`, assumed to be inside the box, is propagated at the level + * of _the closest among the sides of the box_. Note that this means the + * propagated point might still be not on the surface of the box, even if it + * would later reach it. + * It is anyway guaranteed that `xyzout` is not inside the target box, and + * that at least one of its three coordinates is on the box surface. + */ + void ProjectToBoxEdge(const double xyz[], + const double dxyz[], + const double xlo, + const double xhi, + const double ylo, + const double yhi, + const double zlo, + const double zhi, + double xyzout[]); + + int fShowerInputs = 0; ///< Number of shower inputs to process from + std::vector + fNShowersPerEvent; ///< Number of showers to put in each event of duration fSampleTime; one per showerinput + std::vector fMaxShowers; //< Max number of showers to query, one per showerinput + double fShowerBounds[6] = { + 0., + 0., + 0., + 0., + 0., + 0.}; ///< Boundaries of area over which showers are to be distributed (x(min), x(max), _unused_, y, z(min), z(max) ) + double fToffset_corsika = + 0.; ///< Timing offset to account for propagation time through atmosphere, populated from db + ifdh_ns::ifdh* fIFDH = 0; ///< (optional) flux file handling + + //fcl parameters + double fProjectToHeight = 0.; ///< Height to which particles will be projected [cm] + std::vector fShowerInputFiles; ///< Set of CORSIKA shower data files to use + std::string fShowerCopyType; // ifdh file selection and copying (default) or direct file path + std::vector + fShowerFluxConstants; ///< Set of flux constants to be associated with each shower data file + double fSampleTime = 0.; ///< Duration of sample [s] + double fToffset = 0.; ///< Time offset of sample, defaults to zero (no offset) [s] + std::vector + fBuffBox; ///< Buffer box extensions to cryostat in each direction (6 of them: x_lo,x_hi,y_lo,y_hi,z_lo,z_hi) [cm] + double fShowerAreaExtension = + 0.; ///< Extend distribution of corsika particles in x,z by this much (e.g. 1000 will extend 10 m in -x, +x, -z, and +z) [cm] + sqlite3* fdb[5]; ///< Pointers to sqlite3 database object, max of 5 + double fRandomXZShift = + 0.; ///< Each shower will be shifted by a random amount in xz so that showers won't repeatedly sample the same space [cm] + CLHEP::HepRandomEngine& fGenEngine; + CLHEP::HepRandomEngine& fPoisEngine; + }; +} + diff --git a/sbncode/EventGenerator/WireModGen/FilterPrimarySimChannel_module.cc b/sbncode/EventGenerator/WireModGen/FilterPrimarySimChannel_module.cc new file mode 100644 index 000000000..32c9fd3af --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/FilterPrimarySimChannel_module.cc @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////// +// Class: FilterPrimarySimChannel +// Plugin Type: filter (art v3_05_01) +// File: FilterPrimarySimChannel_module.cc +// +// Module for selecting events based on the presence of data products. +// +// Useful for processing data events, which sometimes have missing +// data products. +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDFilter.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "lardataobj/Simulation/SimChannel.h" + +#include + +namespace sbn { + class FilterPrimarySimChannel; +} + +class sbn::FilterPrimarySimChannel : public art::EDFilter { +public: + explicit FilterPrimarySimChannel(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + FilterPrimarySimChannel(FilterPrimarySimChannel const&) = delete; + FilterPrimarySimChannel(FilterPrimarySimChannel&&) = delete; + FilterPrimarySimChannel& operator=(FilterPrimarySimChannel const&) = delete; + FilterPrimarySimChannel& operator=(FilterPrimarySimChannel&&) = delete; + + // Required functions. + bool filter(art::Event& e) override; + +private: + + // Declare member data here. + art::InputTag fSimChannelLabel; + std::vector fTrackIDs; + bool fVerbose; +}; + + +sbn::FilterPrimarySimChannel::FilterPrimarySimChannel(fhicl::ParameterSet const& p) + : EDFilter{p}, + fSimChannelLabel(p.get("SimChannelLabel")), + fTrackIDs(p.get>("TrackIDs", {1})), + fVerbose(p.get("Verbose", true)) +{} + +bool sbn::FilterPrimarySimChannel::filter(art::Event& e) { + art::ValidHandle> simchan = e.getValidHandle>(fSimChannelLabel); + + for (const sim::SimChannel &c: *simchan) { + const sim::SimChannel::TDCIDEs_t &tides = c.TDCIDEMap(); + + for (const sim::TDCIDE &tide: tides) { + for (const sim::IDE &ide: tide.second) { + if (fVerbose) std::cout << "ide at tdc: " << tide.first << " id: " << ide.trackID << " x: " << ide.x << " y: " << ide.y << " z: " << ide.z << " nelec: " << ide.numElectrons << std::endl; + for (int tid: fTrackIDs) { + if (ide.trackID == tid) { + if (fVerbose) std::cout << "Found matching sim channel for track ID: " << tid << std::endl; + return true; + } + } + } + } + } + + if (fVerbose) std::cout << "No matching sim channel." << std::endl; + + return false; +} + +DEFINE_ART_MODULE(sbn::FilterPrimarySimChannel) diff --git a/sbncode/EventGenerator/WireModGen/FilterPrimarySimEnergyDepo_module.cc b/sbncode/EventGenerator/WireModGen/FilterPrimarySimEnergyDepo_module.cc new file mode 100644 index 000000000..0ac810055 --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/FilterPrimarySimEnergyDepo_module.cc @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////// +// Class: FilterPrimarySimEnergyDepo +// Plugin Type: filter (art v3_05_01) +// File: FilterPrimarySimEnergyDepo_module.cc +// +// Module for selecting events based on the presence of data products. +// +// Useful for processing data events, which sometimes have missing +// data products. +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDFilter.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "lardataobj/Simulation/SimEnergyDeposit.h" +#include "larcorealg/Geometry/GeometryCore.h" +#include "larcore/Geometry/Geometry.h" +#include "larcore/CoreUtils/ServiceUtil.h" +#include "lardataalg/DetectorInfo/DetectorPropertiesStandard.h" +#include "lardata/DetectorInfoServices/DetectorPropertiesService.h" +#include "lardataalg/DetectorInfo/DetectorClocksStandard.h" +#include "lardata/DetectorInfoServices/DetectorClocksService.h" + +#include + +namespace sbn { + class FilterPrimarySimEnergyDepo; +} + +class sbn::FilterPrimarySimEnergyDepo : public art::EDFilter { +public: + explicit FilterPrimarySimEnergyDepo(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + FilterPrimarySimEnergyDepo(FilterPrimarySimEnergyDepo const&) = delete; + FilterPrimarySimEnergyDepo(FilterPrimarySimEnergyDepo&&) = delete; + FilterPrimarySimEnergyDepo& operator=(FilterPrimarySimEnergyDepo const&) = delete; + FilterPrimarySimEnergyDepo& operator=(FilterPrimarySimEnergyDepo&&) = delete; + + // Required functions. + bool filter(art::Event& e) override; + +private: + + // Declare member data here. + art::InputTag fSimEnergyDepoLabel; + std::vector fTrackIDs; + bool fVerbose; +}; + + +sbn::FilterPrimarySimEnergyDepo::FilterPrimarySimEnergyDepo(fhicl::ParameterSet const& p) + : EDFilter{p}, + fSimEnergyDepoLabel(p.get("SimEnergyDepoLabel")), + fTrackIDs(p.get>("TrackIDs", {1})), + fVerbose(p.get("Verbose", true)) +{} + +bool sbn::FilterPrimarySimEnergyDepo::filter(art::Event& e) { + art::ValidHandle> simdepo = e.getValidHandle>(fSimEnergyDepoLabel); + + const geo::GeometryCore *geometry = lar::providerFrom(); + auto const &clock_data = art::ServiceHandle()->DataFor(e); + auto const &dprop = + art::ServiceHandle()->DataFor(e, clock_data); + + for (const sim::SimEnergyDeposit &d: *simdepo) { + if (fVerbose) std::cout << "depo at time: " << d.T() << "x: " << d.X() << " y: " << d.Y() << " z: " << d.Z() << " nelec: " << d.NumElectrons() << " id: " << d.TrackID() << std::endl; + + // check if has correct ID, then check if fiducial and in-time + int match_index = -1; + for (unsigned i = 0; i < fTrackIDs.size(); i++) { + if (d.TrackID() == fTrackIDs[i]) { + match_index = (int)i; + } + } + if (match_index == -1) continue; + + for (auto const &cryo: geometry->Iterate()) { + for (auto const& TPC : geometry->Iterate(cryo.ID())) { + if (TPC.ContainsPosition(d.MidPoint())) { + // check drifted point in time + + geo::PlaneID plane(TPC.ID(), 0); // check against front plane + + // get the tick of the point. Subtract out the trigger time, which gets put into t_tick below + double tick = dprop.ConvertXToTicks(d.X(), plane); + + // convert the gen time to a TDC + double t_tdc = clock_data.TPCTick2TDC(clock_data.Time2Tick(d.T()/1e3)); + + // add in the offset from the drift + double x_tick = t_tdc + tick; + + if (fVerbose) std::cout << "Gen time: " << d.T() << " is at TPC tdc: " << t_tdc << ". After drift from X=" << d.X() << " arrival tick is: " << x_tick << ". TPC window is 0 to " << dprop.NumberTimeSamples() << std::endl; + + if ((x_tick > 0) && (x_tick < dprop.NumberTimeSamples())) { + if (fVerbose) std::cout << "Found matching deposit for track ID: " << fTrackIDs[match_index] << std::endl; + return true; + } + + } + } + } + } + + if (fVerbose) std::cout << "No matching deposit." << std::endl; + + return false; +} + +DEFINE_ART_MODULE(sbn::FilterPrimarySimEnergyDepo) diff --git a/sbncode/EventGenerator/WireModGen/WireModMuon_module.cc b/sbncode/EventGenerator/WireModGen/WireModMuon_module.cc new file mode 100644 index 000000000..ae326c1b4 --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/WireModMuon_module.cc @@ -0,0 +1,322 @@ +//////////////////////////////////////////////////////////////////////// +///// \file WireModMuon_module.cc +///// \brief Generates one muon hitting a bounding box, for use by WireMod +///// +///// \author gputnam@fnal.gov +////////////////////////////////////////////////////////////////////////// + +#include "sbncode/EventGenerator/WireModGen/CORSIKAGenSBN.h" + +#include "larcore/CoreUtils/ServiceUtil.h" +#include "larcoreobj/SimpleTypesAndConstants/geo_vectors.h" +#include "lardataalg/DetectorInfo/DetectorPropertiesStandard.h" +#include "lardata/DetectorInfoServices/DetectorPropertiesService.h" +#include "lardataalg/DetectorInfo/DetectorClocksStandard.h" +#include "lardata/DetectorInfoServices/DetectorClocksService.h" + +namespace sbn { + +class WireModMuon: public evgen::CORSIKAGenSBN { + public: + void produce(art::Event& evt) override; + void endRun(art::Run& run) override; + explicit WireModMuon(fhicl::ParameterSet const& pset); + + private: + bool GetMuon(const art::Event &e, const simb::MCTruth &intruth, simb::MCParticle &outparticle); + // Determine if particle ray should be selected + bool SelectRay(const art::Event &e, const TLorentzVector &pos, const TLorentzVector &mom); + // Whether a point is in the TPC readout time + bool PointInTime(const art::Event &e, const geo::TPCID &tpcid, const TVector3 &p, float t); + + struct Plane { + std::array pts; + + bool Intersects(const TVector3 &pos, const TVector3 &dir) const; + bool Intersects(const geo::Point_t &pos, const geo::Vector_t &dir) const; + }; + + // Digested geometry information + std::vector crt_planes; + std::vector tpc_bounds; + + // Enable for debugging output + bool fVerbose; + + // Selection configuration + bool fSelectCXR; + bool fSelectCRT; + unsigned fPrescaleCXR; + unsigned fPrescaleCRT; + unsigned fNMuonPerEvent; + + // Counters + unsigned fNCXR; + unsigned fNCRT; + + unsigned fNSelCXR; + unsigned fNSelCRT; + + unsigned fNCORSIKA; +}; + +} + +sbn::WireModMuon::WireModMuon(fhicl::ParameterSet const& pset): + CORSIKAGenSBN(pset) +{ + + // Load the CRT planes + std::vector crt_x0s = pset.get>("CRTX0", {}); + std::vector crt_x1s = pset.get>("CRTX1", {}); + std::vector crt_y0s = pset.get>("CRTY0", {}); + std::vector crt_y1s = pset.get>("CRTY1", {}); + std::vector crt_z0s = pset.get>("CRTZ0", {}); + std::vector crt_z1s = pset.get>("CRTZ1", {}); + + for (unsigned i_crt = 0; i_crt < crt_x0s.size(); i_crt++) { + std::array ps; + + if (crt_x0s[i_crt] == crt_x1s[i_crt] || crt_y0s[i_crt] == crt_y1s[i_crt]) { + geo::Point_t p1 {crt_x0s[i_crt], crt_y0s[i_crt], crt_z0s[i_crt]}; + geo::Point_t p2 {crt_x1s[i_crt], crt_y1s[i_crt], crt_z0s[i_crt]}; + geo::Point_t p3 {crt_x0s[i_crt], crt_y0s[i_crt], crt_z1s[i_crt]}; + geo::Point_t p4 {crt_x1s[i_crt], crt_y1s[i_crt], crt_z1s[i_crt]}; + ps = {p1, p2, p3, p4}; + } + else { + geo::Point_t p1 {crt_x0s[i_crt], crt_y0s[i_crt], crt_z0s[i_crt]}; + geo::Point_t p2 {crt_x0s[i_crt], crt_y1s[i_crt], crt_z0s[i_crt]}; + geo::Point_t p3 {crt_x1s[i_crt], crt_y0s[i_crt], crt_z1s[i_crt]}; + geo::Point_t p4 {crt_x1s[i_crt], crt_y1s[i_crt], crt_z1s[i_crt]}; + ps = {p1, p2, p3, p4}; + } + + sbn::WireModMuon::Plane crt_plane {ps}; + crt_planes.push_back(crt_plane); + } + + // Load selection config + fSelectCXR = pset.get("SelectCXR", true); + fSelectCRT = pset.get("SelectCRT", true); + + fPrescaleCXR = pset.get("PrescaleCXR", 1); + fPrescaleCRT = pset.get("PrescaleCRT", 1); + + fNMuonPerEvent = pset.get("NMuonPerEvent", 1); + + fNCXR = 0; + fNCRT = 0; + + fNSelCXR = 0; + fNSelCRT = 0; + + fNCORSIKA = 0; + + // debug output config + fVerbose = pset.get("Verbose", true); + +} + +void sbn::WireModMuon::endRun(art::Run& run) { + (void) run; + + mf::LogInfo("WireModMuon") << "Selected " << fNSelCXR << " of " << fNCXR << " cathode crossers and " << fNSelCRT << " of " << fNCRT << " CRT plane crossers. Called CORSIKA " << fNCORSIKA << " times.\n"; +} + +void sbn::WireModMuon::produce(art::Event& evt) { + // output + std::unique_ptr> truthcol(new std::vector); + + simb::MCTruth truth; + truth.SetOrigin(simb::kCosmicRay); + + for (unsigned i_muon = 0; i_muon < fNMuonPerEvent; i_muon++) { + // found a muon + bool foundmuon = false; + simb::MCParticle particle; + + while (!foundmuon) { + simb::MCTruth pretruth; + GetSample(pretruth); + fNCORSIKA ++; + + foundmuon = GetMuon(evt, pretruth, particle); + } + + mf::LogInfo("WireModMuon") << "Selected muon at pos(" << particle.Vx() << ", " << particle.Vy() << ", " << particle.Vz() << ") [cm], dir(" + << particle.Momentum().Vect().Unit().X() << ", " << particle.Momentum().Vect().Unit().Y() << ", " << particle.Momentum().Vect().Unit().Z() + << "), with momentum " << particle.Momentum().Vect().Mag() << " [GeV/c].\n"; + + truth.Add(particle); + } + + // save output + truthcol->push_back(truth); + evt.put(std::move(truthcol)); + + return; +} + +bool sbn::WireModMuon::PointInTime(const art::Event &e, const geo::TPCID &tpcid, const TVector3 &p, float t) { + auto const &clock_data = art::ServiceHandle()->DataFor(e); + auto const &dprop = + art::ServiceHandle()->DataFor(e, clock_data); + + if (fVerbose) std::cout << "Point: " << p.X() << " " << p.Y() << " " << p.Z() << " is in TPC: " << tpcid << std::endl; + + if (!tpcid) return false; + + geo::PlaneID plane(tpcid, 0); // check against front plane + + // get the tick of the point. Subtract out the trigger time, which gets put into t_tick below + double tick = dprop.ConvertXToTicks(p.X(), plane); + + // convert the gen time to a TDC + double t_tdc = clock_data.TPCTick2TDC(clock_data.Time2Tick(t/1e3)); + + // add in the offset from the drift + double x_tick = t_tdc + tick; + + if (fVerbose) std::cout << "Gen time: " << t << " is at TPC tdc: " << t_tdc << ". After drift from X=" << p.X() << " arrival tick is: " << x_tick << ". TPC window is 0 to " << dprop.NumberTimeSamples() << std::endl; + + // this needs to be inside the tpc window + return (x_tick > 0) && (x_tick < dprop.NumberTimeSamples()); +} + +bool sbn::WireModMuon::SelectRay(const art::Event &e, const TLorentzVector &pos, const TLorentzVector &mom) { + // load services + const geo::GeometryCore *geometry = lar::providerFrom(); + + bool CXR = false; + bool CRT = false; + + TVector3 dir = mom.Vect().Unit(); + TVector3 pos3 = pos.Vect(); + const double c_cm_per_ns = 29.9792; + double vel = mom.Beta()*c_cm_per_ns; + const double eps = 1e-1; // wiggle room to ensure inside volume + + for (auto const &cryo: geometry->Iterate()) { + for (auto const& TPC : geometry->Iterate(cryo.ID())) { + std::vector intersections = TPC.ActiveBoundingBox().GetIntersections(pos3, dir); + const double check_eps = 1e-1; + float cathode = (TPC.DriftDir().X() > 0) ? TPC.ActiveBoundingBox().MinX() : TPC.ActiveBoundingBox().MaxX(); + CXR = intersections.size() == 2 && ((abs(intersections[0].X() - cathode) < check_eps) || (abs(intersections[1].X() - cathode) < check_eps)); + + // check if cathode point is in-time + if (CXR) { + TVector3 cathode_point = (abs(intersections[0].X() - cathode) < abs(intersections[1].X() - cathode)) ? (intersections[0] + dir*eps) : (intersections[1] - dir*eps); + + if (fVerbose) std::cout << "Ray with initial time: " << pos.T() << " and arrival time: " << (pos.T() + cathode_point.Mag() / vel) << " with vel: " << vel << std::endl; + if (fVerbose) std::cout << "Checking if cathode intersection point: " << cathode_point.X() << " " << cathode_point.Y() << " " << cathode_point.Z() << " is in time." << std::endl; + CXR = PointInTime(e, TPC.ID(), cathode_point, pos.T() + (cathode_point - pos3).Mag() / vel); + } + if (CXR) break; + } + if (CXR) break; + } + if (fVerbose) std::cout << "CXR: " << CXR << std::endl; + + for (const sbn::WireModMuon::Plane &crt: crt_planes) { + CRT = crt.Intersects(pos3, dir); + if (CRT) { + // require intersect active volume + // + // and require either entrance or exit point to be in-time + for (auto const &cryo: geometry->Iterate()) { + for (auto const& TPC : geometry->Iterate(cryo.ID())) { + std::vector intersections = TPC.ActiveBoundingBox().GetIntersections(pos3, dir); + CRT = intersections.size() == 2; + if (CRT) { + if (fVerbose) std::cout << "Ray with initial time: " << pos.T() << " and arrival time: " << (pos.T() + (intersections[0] - pos3).Mag() / vel) << " with vel: " << vel << std::endl; + CRT = PointInTime(e, TPC.ID(), intersections[0] + dir*eps, pos.T() + (intersections[0] - pos3).Mag() / vel) || + PointInTime(e, TPC.ID(), intersections[1] - dir*eps, pos.T() + (intersections[1] - pos3).Mag() / vel); + } + if (CRT) break; + } + if (CRT) break; + } + } + if (CRT) break; + } + if (fVerbose) std::cout << "CRT: " << CRT << std::endl; + + if (CXR) fNCXR ++; + if (CRT) fNCRT ++; + + if (CXR && fSelectCXR && (fNCXR % fPrescaleCXR == 0)) { + fNSelCXR ++; + return true; + } + + if (CRT && fSelectCRT && (fNCRT % fPrescaleCRT == 0)) { + fNSelCRT ++; + return true; + } + + return false; + +} + +bool sbn::WireModMuon::GetMuon(const art::Event &e, const simb::MCTruth &intruth, simb::MCParticle &outparticle) { + for (int i = 0; i < intruth.NParticles(); ++i) { + const simb::MCParticle &particle = intruth.GetParticle(i); + if (abs(particle.PdgCode()) != 13) continue; // only muons + + if (fVerbose) std::cout << "Found muon at pos(" << particle.Vx() << ", " << particle.Vy() << ", " << particle.Vz() << ") [cm], dir(" + << particle.Momentum().Vect().Unit().X() << ", " << particle.Momentum().Vect().Unit().Y() << ", " << particle.Momentum().Vect().Unit().Z() + << "), with momentum " << particle.Momentum().Vect().Mag() << " [GeV/c].\n"; + + if (SelectRay(e, particle.Position(), particle.Momentum())) { + outparticle = particle; + return true; + } + } + + return false; +} + +bool sbn::WireModMuon::Plane::Intersects(const TVector3 &pos, const TVector3 &dir) const { + geo::Point_t pos_t(pos.X(), pos.Y(), pos.Z()); + geo::Vector_t dir_t(dir.X(), dir.Y(), dir.Z()); + return Intersects(pos_t, dir_t); +} + +bool sbn::WireModMuon::Plane::Intersects(const geo::Point_t &pos, const geo::Vector_t &dir) const { + // Get the u, v vectors + geo::Vector_t u = (geo::Vector_t(pts[1]) - geo::Vector_t(pts[0])).Unit(); + geo::Vector_t v = (geo::Vector_t(pts[2]) - geo::Vector_t(pts[0])).Unit(); + double ulen = (geo::Vector_t(pts[1]) - geo::Vector_t(pts[0])).Dot(u); + double vlen = (geo::Vector_t(pts[2]) - geo::Vector_t(pts[0])).Dot(v); + + // calculate the normal of the plane + geo::Vector_t normal = u.Cross(v); + + double denom = dir.Dot(normal); + + if (abs(denom) < 1e-5) { // parallel to plane + return false; + } + + // point along ray where it intersects plane + double t = (pts[3] - pos).Dot(normal) / denom; + + // intersection happens behind the ray + if (t < 0) { + return false; + } + + geo::Vector_t intersection = pos + dir*t - pts[0]; + + double int_u = intersection.Dot(u); + double int_v = intersection.Dot(v); + + // if (fVerbose) std::cout << "Plane check. ulen: " << ulen << " vlen: " << vlen << ". u: " << int_u << " v: " << int_v << " t: " << t << std::endl; + + return (int_u > 0) && (int_u < ulen) && (int_v > 0) && (int_v < vlen); +} + +namespace sbn { + DEFINE_ART_MODULE(WireModMuon) +} diff --git a/sbncode/EventGenerator/WireModGen/filter_wiremod_sbn.fcl b/sbncode/EventGenerator/WireModGen/filter_wiremod_sbn.fcl new file mode 100644 index 000000000..e04c0f49c --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/filter_wiremod_sbn.fcl @@ -0,0 +1,9 @@ +BEGIN_PROLOG + +filter_primary_simchan: { + module_type: FilterPrimarySimEnergyDepo + SimEnergyDepoLabel: ionization + TrackIDs: [1, 2, 3] +} + +END_PROLOG diff --git a/sbncode/EventGenerator/WireModGen/wiremodmuon_sbn.fcl b/sbncode/EventGenerator/WireModGen/wiremodmuon_sbn.fcl new file mode 100644 index 000000000..9ab3ebf5d --- /dev/null +++ b/sbncode/EventGenerator/WireModGen/wiremodmuon_sbn.fcl @@ -0,0 +1,26 @@ +BEGIN_PROLOG + +# needs the corsika config to be added to the table, inside the experimental codebase +wiremodmuon_icarus: { + module_type: WireModMuon + + # CRT bounds + # + # Each entry (in X0, X1, Y0, Y1, Z0, Z1) is one plane that will be included in the intersection check + # + # listed as: Top horizontal, Top Rim West, Top Rim East, Top Rim South + CRTX0: [-580, 555, -555, -560, -560] + CRTX1: [580, 555, -555, 380, 560] + CRTY0: [617.4, 400, 400, 400, 420] + CRTY1: [617.4, 600, 600, 600, 620] + CRTZ0: [-1180, -1180, -1180, -1143, 1533] + CRTZ1: [1460, 1460, 1460, -1143, 1533] + + # selection config + SelectA2C: true + SelectCRT: true + PrescaleA2C: 1 + PrescaleCRT: 1 + NMuonPerEvent: 3 +} +END_PROLOG diff --git a/sbncode/FlashMatch/CMakeLists.txt b/sbncode/FlashMatch/CMakeLists.txt index 015322e49..9cfd8588b 100644 --- a/sbncode/FlashMatch/CMakeLists.txt +++ b/sbncode/FlashMatch/CMakeLists.txt @@ -36,7 +36,7 @@ set( MODULE_LIBRARIES ROOT::Gdml ) -cet_build_plugin(FlashPredict art::module LIBRARIES ${MODULE_LIBRARIES}) +cet_build_plugin(FlashPredict art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) install_headers() diff --git a/sbncode/FluxReader/CMakeLists.txt b/sbncode/FluxReader/CMakeLists.txt index ad70d3c22..0aa8b218c 100644 --- a/sbncode/FluxReader/CMakeLists.txt +++ b/sbncode/FluxReader/CMakeLists.txt @@ -4,7 +4,7 @@ cet_enable_asserts() file(GLOB INTERFACEFILES *Interface* ) cet_build_plugin( FluxReader art::source - LIBRARIES + LIBRARIES PRIVATE sbncode_FluxReader larcore::Geometry_Geometry_service nutools::EventGeneratorBase_CRY @@ -12,7 +12,7 @@ cet_build_plugin( FluxReader art::source ) cet_build_plugin ( FluxGeoFilter art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Principal art::Framework_Services_Registry @@ -71,7 +71,7 @@ cet_make_library( lardata::Utilities ) -cet_build_plugin(FluxReaderAna art::module LIBRARIES +cet_build_plugin(FluxReaderAna art::module LIBRARIES PRIVATE sbncode_FluxReader sbncode_FluxReader_FluxInterface nusimdata::SimulationBase diff --git a/sbncode/HitFinder/CMakeLists.txt b/sbncode/HitFinder/CMakeLists.txt new file mode 100644 index 000000000..a150cc14c --- /dev/null +++ b/sbncode/HitFinder/CMakeLists.txt @@ -0,0 +1,58 @@ +add_subdirectory(HitFinderUtilities) + +art_make_library( + LIBRARIES + lardataobj::RawData + lardataobj::RecoBase + lardata::Utilities + fhiclcpp::fhiclcpp + cetlib::cetlib +) +set( MODULE_LIBRARIES + larcorealg::Geometry + larcorealg::Geometry + larcore::Geometry_Geometry_service + lardata::Utilities + larevt::Filters + lardataobj::RawData + larevt::CalibrationDBI_IOVData + larevt::CalibrationDBI_Providers + lardataobj::RecoBase + lardata::ArtDataHelper + larreco::RecoAlg + sbnobj::ICARUS_TPC + sbnobj::Common_Utilities + sbncode::HitFinder_HitFinderUtilities + art::Framework_Core + art::Framework_Principal + art::Framework_Services_Registry + art_root_io::tfile_support ROOT::Core + art_root_io::TFileService_service + art::Persistency_Common + art::Persistency_Provenance + art::Utilities + canvas::canvas + messagefacility::MF_MessageLogger + messagefacility::headers + fhiclcpp::fhiclcpp + cetlib::cetlib + ROOT::Geom + ROOT::XMLIO + ROOT::Gdml + ROOT::FFTW + + ) +cet_build_plugin(GaussHitFinderSBN art::module + LIBRARIES PRIVATE ${MODULE_LIBRARIES} + larreco::HitFinder + larreco::CandidateHitFinderTool + larreco::PeakFitterTool + ) +cet_build_plugin(ChannelROIToWire art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(WireToChannelROI art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) + + +install_headers() +install_fhicl() +install_source() + diff --git a/sbncode/HitFinder/ChannelROIToWire_module.cc b/sbncode/HitFinder/ChannelROIToWire_module.cc new file mode 100644 index 000000000..42d304537 --- /dev/null +++ b/sbncode/HitFinder/ChannelROIToWire_module.cc @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////////////////// +// +// ChannelROIToWire class - Converts from ChannelROI to Wire data +// +// usher@slac.stanford.edu +// +//////////////////////////////////////////////////////////////////////// + +// C/C++ standard libraries +#include +#include +#include // std::pair<> +#include // std::unique_ptr<> + +// framework libraries +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "canvas/Utilities/Exception.h" +#include "canvas/Utilities/InputTag.h" + +// LArSoft libraries +#include "larcoreobj/SimpleTypesAndConstants/RawTypes.h" // raw::ChannelID_t +#include "larcore/Geometry/WireReadout.h" +#include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom() +#include "larcorealg/CoreUtils/zip.h" +#include "lardataobj/RecoBase/Wire.h" +#include "lardata/ArtDataHelper/WireCreator.h" + +#include "sbnobj/ICARUS/TPC/ChannelROI.h" + +///creation of calibrated signals on wires +namespace caldata { + +class ChannelROIToWire : public art::EDProducer +{ +public: +// create calibrated signals on wires. this class runs +// an fft to remove the electronics shaping. + explicit ChannelROIToWire(fhicl::ParameterSet const& pset); + void produce(art::Event& evt); + void beginJob(); + void endJob(); + void reconfigure(fhicl::ParameterSet const& p); +private: + + std::vector fWireModuleLabelVec; ///< vector of modules that made digits + std::vector fOutInstanceLabelVec; ///< The output instance labels to apply + bool fDiagnosticOutput; ///< secret diagnostics flag + size_t fEventCount; ///< count of event processed + + const geo::WireReadoutGeom* fChannelMapAlg = &art::ServiceHandle()->Get(); + +}; // class ChannelROIToWire + +DEFINE_ART_MODULE(ChannelROIToWire) + +//------------------------------------------------- +ChannelROIToWire::ChannelROIToWire(fhicl::ParameterSet const& pset) : EDProducer{pset} +{ + this->reconfigure(pset); + + for(const auto& wireLabel : fOutInstanceLabelVec) + { + produces>(wireLabel); + } +} + +////////////////////////////////////////////////////// +void ChannelROIToWire::reconfigure(fhicl::ParameterSet const& pset) +{ + // Recover the parameters + fWireModuleLabelVec = pset.get>("WireModuleLabelVec", std::vector()={"decon1droi"}); + fOutInstanceLabelVec = pset.get> ("OutInstanceLabelVec", {"PHYSCRATEDATA"}); + fDiagnosticOutput = pset.get< bool >("DiagnosticOutput", false); + + if (fWireModuleLabelVec.size() != fOutInstanceLabelVec.size()) + { + throw art::Exception(art::errors::Configuration) << " Configured " << fOutInstanceLabelVec.size() + << " instance names (`OutInstanceLabelVec`) for " << fWireModuleLabelVec.size() + << " input products (`WireModuleLabelVec`)\n"; + } + + return; +} + +//------------------------------------------------- +void ChannelROIToWire::beginJob() +{ + fEventCount = 0; +} // beginJob + +////////////////////////////////////////////////////// +void ChannelROIToWire::endJob() +{ +} + +////////////////////////////////////////////////////// +void ChannelROIToWire::produce(art::Event& evt) +{ + // We need to loop through the list of Wire data we have been given + // This construct from Gianluca Petrillo who invented it and should be given all credit for it! + for(auto const& [channelLabel, instanceName] : util::zip(fWireModuleLabelVec, fOutInstanceLabelVec)) + { + // make a collection of Wires + std::unique_ptr> wireCol = std::make_unique>(); + + mf::LogInfo("ChannelROIToWire") << "ChannelROIToWire, looking for ChannelROI data at " << channelLabel.encode(); + + // Read in the collection of full length deconvolved waveforms + const std::vector& channelVec = evt.getProduct>(channelLabel); + + mf::LogInfo("ChannelROIToWire") << "--> Recovered ChannelROI data, size: " << channelVec.size(); + + if (!channelVec.empty()) + { + // Reserve the room for the output + wireCol->reserve(channelVec.size()); + + // Loop through the input ChannelROI collection + for(const auto& channelROI : channelVec) + { + // Recover the channel and the view + raw::ChannelID_t channel = channelROI.Channel(); + geo::View_t view = fChannelMapAlg->View(channel); + float ADCScaleFactor = channelROI.ADCScaleFactor(); + + // Create an ROI vector for output + recob::Wire::RegionsOfInterest_t ROIVec; + + // Loop through the ROIs for this channel + const recob::ChannelROI::RegionsOfInterest_t& channelROIs = channelROI.SignalROI(); + + for(const auto& range : channelROIs.get_ranges()) + { + size_t startTick = range.begin_index(); + + std::vector dataVec(range.data().size()); + + for(size_t binIdx = 0; binIdx < range.data().size(); binIdx++) dataVec[binIdx] = std::round(range.data()[binIdx] / ADCScaleFactor); + + ROIVec.add_range(startTick, std::move(dataVec)); + } + + wireCol->push_back(recob::WireCreator(std::move(ROIVec),channel,view).move()); + } + + mf::LogInfo("ChannelROIToWire") << "--> Outputting Wire data, size: " << wireCol->size() << " with instance name: " << instanceName; + + // Time to stroe everything + if(wireCol->empty()) mf::LogWarning("ChannelROIToWire") << "No wires made for this event."; + } + + evt.put(std::move(wireCol), instanceName); + } + + fEventCount++; + + return; +} // produce + +} // end namespace caldata diff --git a/sbncode/HitFinder/GaussHitFinderSBN_module.cc b/sbncode/HitFinder/GaussHitFinderSBN_module.cc new file mode 100644 index 000000000..6d5147043 --- /dev/null +++ b/sbncode/HitFinder/GaussHitFinderSBN_module.cc @@ -0,0 +1,688 @@ +//////////////////////////////////////////////////////////////////////// +// +// GaussHitFinder class +// +// jaasaadi@syr.edu +// +// This algorithm is designed to find hits on wires after deconvolution. +// ----------------------------------- +// This algorithm is based on the FFTHitFinder written by Brian Page, +// Michigan State University, for the ArgoNeuT experiment. +// +// +// The algorithm walks along the wire and looks for pulses above threshold +// The algorithm then attempts to fit n-gaussians to these pulses where n +// is set by the number of peaks found in the pulse +// If the Chi2/NDF returned is "bad" it attempts to fit n+1 gaussians to +// the pulse. If this is a better fit it then uses the parameters of the +// Gaussian fit to characterize the "hit" object +// +// To use this simply include the following in your producers: +// gaushit: @local::microboone_GaussHitFinderSBN +// gaushit: @local::argoneut_GaussHitFinderSBN +//////////////////////////////////////////////////////////////////////// + +// C/C++ standard library +#include // std::accumulate() +#include +#include // std::unique_ptr() +#include +#include // std::move() + +// Framework includes +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Core/SharedProducer.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "art/Utilities/Globals.h" +#include "art/Utilities/make_tool.h" +#include "art_root_io/TFileService.h" +#include "canvas/Persistency/Common/FindOneP.h" +#include "fhiclcpp/ParameterSet.h" + +// LArSoft Includes +#include "larcore/Geometry/WireReadout.h" +#include "larcoreobj/SimpleTypesAndConstants/RawTypes.h" // raw::ChannelID_t +#include "larreco/HitFinder/HitFilterAlg.h" +#include "lardataobj/RecoBase/Hit.h" + +#include "sbnobj/ICARUS/TPC/ChannelROI.h" +#include "sbncode/HitFinder/HitFinderUtilities/HitCreator.h" + +#include "larreco/HitFinder/HitFinderTools/ICandidateHitFinder.h" +#include "larreco/HitFinder/HitFinderTools/IPeakFitter.h" + +// ROOT Includes +#include "TH1F.h" +#include "TMath.h" + +#include "tbb/concurrent_vector.h" +#include "tbb/parallel_for.h" + +namespace hit { + class GaussHitFinderSBN : public art::SharedProducer { + public: + explicit GaussHitFinderSBN(fhicl::ParameterSet const& pset, art::ProcessingFrame const&); + + private: + void produce(art::Event& evt, art::ProcessingFrame const&) override; + void beginJob(art::ProcessingFrame const&) override; + + std::vector FillOutHitParameterVector(const std::vector& input); + + template + std::vector getValueOrListOf(fhicl::ParameterSet const& pset, std::string const& key) const { + + auto const& wireReadoutGeom = art::ServiceHandle()->Get(); + const unsigned int N_PLANES = wireReadoutGeom.Nplanes(); + + if (pset.is_key_to_sequence(key)) { + auto vec = pset.get>(key); + if (vec.size() != N_PLANES) { + throw cet::exception("Configuration") + << "Parameter '" << key << "' has size " << vec.size() + << " but expected " << N_PLANES << " (number of planes)"; + } + return pset.get>(key); + } else + return std::vector(N_PLANES, pset.get(key)); + } // getValueOrListOf() + + const bool fFilterHits; + const bool fFillHists; + + const std::string fCalDataModuleLabel; + const std::string fAllHitsInstanceName; + + const std::vector fLongMaxHitsVec; ///< Maximum number hits on a really long pulse train + const std::vector fLongPulseWidthVec; ///< Sets width of hits used to describe long pulses + + const std::vector fMaxMultiHit; ///< Maximum hits for multi fit + const int fAreaMethod; ///< Type of area calculation + const std::vector + fAreaNormsVec; ///< Factors for converting area to same units as peak height + const std::vector fChi2NDF; ///< Maximum Chisquared / NDF allowed for a hit to be saved + + const std::vector fPulseHeightCuts; + const std::vector fPulseWidthCuts; + const std::vector fPulseRatioCuts; + + std::atomic fEventCount{0}; + + //only Standard and Morphological implementation is threadsafe. + std::vector> + fHitFinderToolVec; ///< For finding candidate hits + // only Marqdt implementation is threadsafe. + std::unique_ptr fPeakFitterTool; ///< Perform fit to candidate peaks + //HitFilterAlg implementation is threadsafe. + std::unique_ptr fHitFilterAlg; ///< algorithm used to filter out noise hits + + //only used when fFillHists is true and in single threaded mode. + TH1F* fFirstChi2; + TH1F* fChi2; + + }; // class GaussHitFinderSBN + + //------------------------------------------------- + //------------------------------------------------- + GaussHitFinderSBN::GaussHitFinderSBN(fhicl::ParameterSet const& pset, art::ProcessingFrame const&) + : SharedProducer{pset} + , fFilterHits(pset.get("FilterHits", false)) + , fFillHists(pset.get("FillHists", false)) + , fCalDataModuleLabel(pset.get("CalDataModuleLabel")) + , fAllHitsInstanceName(pset.get("AllHitsInstanceName", "")) + , fLongMaxHitsVec(pset.get>("LongMaxHits", std::vector() = {25, 25, 25})) + , fLongPulseWidthVec( + pset.get>("LongPulseWidth", std::vector() = {16, 16, 16})) + , fMaxMultiHit(getValueOrListOf(pset, "MaxMultiHit")) + , fAreaMethod(pset.get("AreaMethod")) + , fAreaNormsVec(FillOutHitParameterVector(pset.get>("AreaNorms"))) + , fChi2NDF(getValueOrListOf(pset, "Chi2NDF")) + , fPulseHeightCuts( + pset.get>("PulseHeightCuts", std::vector() = {3.0, 3.0, 3.0})) + , fPulseWidthCuts( + pset.get>("PulseWidthCuts", std::vector() = {2.0, 1.5, 1.0})) + , fPulseRatioCuts( + pset.get>("PulseRatioCuts", std::vector() = {0.35, 0.40, 0.20})) + { + + if (fFillHists && art::Globals::instance()->nthreads() > 1u) { + throw art::Exception(art::errors::Configuration) + << "Cannot fill histograms when multiple threads configured, please set fFillHists to " + "false or change number of threads to 1\n"; + } + async(); + if (fFilterHits) { + fHitFilterAlg = std::make_unique(pset.get("HitFilterAlg")); + } + + // recover the tool to do the candidate hit finding + // Recover the vector of fhicl parameters for the ROI tools + const fhicl::ParameterSet& hitFinderTools = pset.get("HitFinderToolVec"); + + fHitFinderToolVec.resize(hitFinderTools.get_pset_names().size()); + + for (const std::string& hitFinderTool : hitFinderTools.get_pset_names()) { + const fhicl::ParameterSet& hitFinderToolParamSet = + hitFinderTools.get(hitFinderTool); + size_t planeIdx = hitFinderToolParamSet.get("Plane"); + + fHitFinderToolVec.at(planeIdx) = + art::make_tool(hitFinderToolParamSet); + } + + // Recover the peak fitting tool + fPeakFitterTool = + art::make_tool(pset.get("PeakFitter")); + + // let HitCollectionCreator declare that we are going to produce + // hits and associations with wires and raw digits + // We want the option to output two hit collections, one filtered + // and one with all hits. The key to doing this will be a non-null + // instance name for the second collection + // (with no particular product label) + sbn::HitCollectionCreator::declare_products( + producesCollector(), fAllHitsInstanceName, true, false); //fMakeRawDigitAssns); + + // and now the filtered hits... + if (fAllHitsInstanceName != "") + sbn::HitCollectionCreator::declare_products( + producesCollector(), "", true, false); //fMakeRawDigitAssns); + + return; + } // GaussHitFinderSBN::GaussHitFinderSBN() + + //------------------------------------------------- + //------------------------------------------------- + std::vector GaussHitFinderSBN::FillOutHitParameterVector(const std::vector& input) + { + if (input.size() == 0) + throw std::runtime_error( + "GaussHitFinderSBN::FillOutHitParameterVector ERROR! Input config vector has zero size."); + + std::vector output; + auto const& wireReadoutGeom = art::ServiceHandle()->Get(); + const unsigned int N_PLANES = wireReadoutGeom.Nplanes(); + + if (input.size() == 1) + output.resize(N_PLANES, input[0]); + else if (input.size() == N_PLANES) + output = input; + else + throw std::runtime_error("GaussHitFinderSBN::FillOutHitParameterVector ERROR! Input config " + "vector size !=1 and !=N_PLANES."); + return output; + } + + //------------------------------------------------- + //------------------------------------------------- + void GaussHitFinderSBN::beginJob(art::ProcessingFrame const&) + { + // get access to the TFile service + art::ServiceHandle tfs; + + // ====================================== + // === Hit Information for Histograms === + if (fFillHists) { + fFirstChi2 = tfs->make("fFirstChi2", "#chi^{2}", 10000, 0, 5000); + fChi2 = tfs->make("fChi2", "#chi^{2}", 10000, 0, 5000); + } + } + + // This algorithm uses the fact that deconvolved signals are very smooth + // and looks for hits as areas between local minima that have signal above + // threshold. + //------------------------------------------------- + void GaussHitFinderSBN::produce(art::Event& evt, art::ProcessingFrame const&) + { + unsigned int count = fEventCount.fetch_add(1); + //================================================================================================== + + TH1::AddDirectory(kFALSE); + + auto const& wireReadoutGeom = art::ServiceHandle()->Get(); + + // ############################################### + // ### Making a ptr vector to put on the event ### + // ############################################### + // this contains the hit collection + // and its associations to wires and raw digits + sbn::HitCollectionCreator allHitCol(evt, fAllHitsInstanceName, true, false); + + // Handle the filtered hits collection... + sbn::HitCollectionCreator hcol(evt, "", true, false); + sbn::HitCollectionCreator* filteredHitCol = 0; + + if (fFilterHits) filteredHitCol = &hcol; + + //store in a thread safe way + struct hitstruct { + recob::Hit hit_tbb; + art::Ptr channelROI_tbb; + }; + + tbb::concurrent_vector hitstruct_vec; + tbb::concurrent_vector filthitstruct_vec; + + // ########################################## + // ### Reading in the Wire List object(s) ### + // ########################################## + art::Handle> channelROIVecHandle; + evt.getByLabel(fCalDataModuleLabel, channelROIVecHandle); + + //################################################# + //### Set the charge determination method ### + //### Default is to compute the normalized area ### + //################################################# + std::function chargeFunc = + [](double /* peakMean */, + double peakAmp, + double peakWidth, + double areaNorm, + int /* low */, + int /* hi */) { return std::sqrt(2 * TMath::Pi()) * peakAmp * peakWidth / areaNorm; }; + + //############################################## + //### Alternative is to integrate over pulse ### + //############################################## + if (fAreaMethod == 0) + chargeFunc = [](double peakMean, + double peakAmp, + double peakWidth, + double /* areaNorm */, + int low, + int hi) { + double charge(0); + for (int sigPos = low; sigPos < hi; sigPos++) + charge += peakAmp * TMath::Gaus(sigPos, peakMean, peakWidth); + return charge; + }; + + //############################## + //### Looping over the wires ### + //############################## + tbb::parallel_for( + static_cast(0), + channelROIVecHandle->size(), + [&](size_t& channelROIIter) { + // #################################### + // ### Getting this particular channelROI ### + // #################################### + art::Ptr channelROI(channelROIVecHandle, channelROIIter); + + // --- Setting Channel Number and Signal type --- + + raw::ChannelID_t channel = channelROI->Channel(); + + // get the WireID for this hit + std::vector wids = wireReadoutGeom.ChannelToWire(channel); + // for now, just take the first option returned from ChannelToWire + geo::WireID wid = wids[0]; + // We need to know the plane to look up parameters + geo::PlaneID::PlaneID_t plane = wid.Plane; + + // ---------------------------------------------------------- + // -- Setting the appropriate signal widths and thresholds -- + // -- for the right plane. -- + // ---------------------------------------------------------- + + // ################################################# + // ### Set up to loop over ROI's for this wire ### + // ################################################# + const recob::ChannelROI::RegionsOfInterest_f signalROI = channelROI->SignalROIF(); + + tbb::parallel_for( + static_cast(0), + signalROI.n_ranges(), + [&](size_t& rangeIter) { + const auto& range = signalROI.range(rangeIter); + // ROI start time + raw::TDCtick_t roiFirstBinTick = range.begin_index(); + + // ########################################################### + // ### Scan the waveform and find candidate peaks + merge ### + // ########################################################### + + reco_tool::ICandidateHitFinder::HitCandidateVec hitCandidateVec; + reco_tool::ICandidateHitFinder::MergeHitCandidateVec mergedCandidateHitVec; + + fHitFinderToolVec.at(plane)->findHitCandidates( + range, 0, channel, count, hitCandidateVec); + fHitFinderToolVec.at(plane)->MergeHitCandidates( + range, hitCandidateVec, mergedCandidateHitVec); + + // ####################################################### + // ### Lets loop over the pulses we found on this wire ### + // ####################################################### + + for (auto& mergedCands : mergedCandidateHitVec) { + int startT = mergedCands.front().startTick; + int endT = mergedCands.back().stopTick; + + // ### Putting in a protection in case things went wrong ### + // ### In the end, this primarily catches the case where ### + // ### a fake pulse is at the start of the ROI ### + if (endT - startT < 5) continue; + + // ####################################################### + // ### Clearing the parameter vector for the new Pulse ### + // ####################################################### + + // === Setting The Number Of Gaussians to try === + int nGausForFit = mergedCands.size(); + + // ################################################## + // ### Calling the function for fitting Gaussians ### + // ################################################## + double chi2PerNDF(0.); + int NDF(1); + /*stand alone + reco_tool::IPeakFitter::PeakParamsVec peakParamsVec(nGausForFit); + */ + reco_tool::IPeakFitter::PeakParamsVec peakParamsVec; + + // ####################################################### + // ### If # requested Gaussians is too large then punt ### + // ####################################################### + if (mergedCands.size() <= fMaxMultiHit.at(plane)) { + fPeakFitterTool->findPeakParameters( + range.data(), mergedCands, peakParamsVec, chi2PerNDF, NDF); + + // If the chi2 is infinite then there is a real problem so we bail + if (!(chi2PerNDF < std::numeric_limits::infinity())) { + chi2PerNDF = 2. * fChi2NDF.at(plane); + NDF = 2; + } + + if (fFillHists) fFirstChi2->Fill(chi2PerNDF); + } + + // ####################################################### + // ### If too large then force alternate solution ### + // ### - Make n hits from pulse train where n will ### + // ### depend on the fhicl parameter fLongPulseWidth ### + // ### Also do this if chi^2 is too large ### + // ####################################################### + if (mergedCands.size() > fMaxMultiHit.at(plane) || nGausForFit * chi2PerNDF > fChi2NDF.at(plane)) { + int longPulseWidth = fLongPulseWidthVec.at(plane); + int nHitsThisPulse = (endT - startT) / longPulseWidth; + + if (nHitsThisPulse > fLongMaxHitsVec.at(plane)) { + nHitsThisPulse = fLongMaxHitsVec.at(plane); + longPulseWidth = (endT - startT) / nHitsThisPulse; + } + + if (nHitsThisPulse * longPulseWidth < endT - startT) nHitsThisPulse++; + + int firstTick = startT; + int lastTick = std::min(firstTick + longPulseWidth, endT); + + peakParamsVec.clear(); + nGausForFit = nHitsThisPulse; + NDF = 1.; + chi2PerNDF = chi2PerNDF > fChi2NDF.at(plane) ? chi2PerNDF : -1.; + + for (int hitIdx = 0; hitIdx < nHitsThisPulse; hitIdx++) { + // This hit parameters + double ROIsumADC = + std::accumulate(range.begin() + firstTick, range.begin() + lastTick, 0.); + double peakSigma = (lastTick - firstTick) / 3.; // Set the width... + double peakAmp = 0.3989 * ROIsumADC / peakSigma; // Use gaussian formulation + double peakMean = (firstTick + lastTick) / 2.; + + // Store hit params + reco_tool::IPeakFitter::PeakFitParams_t peakParams; + + peakParams.peakCenter = peakMean; + peakParams.peakCenterError = 0.1 * peakMean; + peakParams.peakSigma = peakSigma; + peakParams.peakSigmaError = 0.1 * peakSigma; + peakParams.peakAmplitude = peakAmp; + peakParams.peakAmplitudeError = 0.1 * peakAmp; + + peakParamsVec.push_back(peakParams); + + // set for next loop + firstTick = lastTick; + lastTick = std::min(lastTick + longPulseWidth, endT); + } + } + + // ####################################################### + // ### Loop through returned peaks and make recob hits ### + // ####################################################### + + int numHits(0); + + // Make a container for what will be the filtered collection + std::vector filteredHitVec; + + float nextpeak(0); + float prevpeak(0); + float nextpeakSig(0); + float prevpeakSig(0); + float nsigmaADC(2.0); + float newright(0); + float newleft(0); + for (const auto& peakParams : peakParamsVec) { + // Extract values for this hit + float peakAmp = peakParams.peakAmplitude; + float peakMean = peakParams.peakCenter; + float peakWidth = peakParams.peakSigma; + + //std::cout<<" ans hits "< 0) { + prevpeak = (peakParamsVec.at(numHits - 1)).peakCenter; + prevpeakSig = (peakParamsVec.at(numHits - 1)).peakSigma; + //std::cout<<" ans size "<::const_iterator sumStartItr = range.begin() + startT; + std::vector::const_iterator sumEndItr = range.begin() + endT; + + //### limits for the sum of the Hit based on the gaussian peak and sigma + std::vector::const_iterator HitsumStartItr = + range.begin() + peakMean - nsigmaADC * peakWidth; + std::vector::const_iterator HitsumEndItr = + range.begin() + peakMean + nsigmaADC * peakWidth; + + if (nGausForFit > 1) { + if (numHits > 0) { + if ((peakMean - nsigmaADC * peakWidth) < (prevpeak + nsigmaADC * prevpeakSig)) { + float difPeak = peakMean - prevpeak; + float weightpeak = prevpeakSig / (prevpeakSig + peakWidth); + HitsumStartItr = range.begin() + prevpeak + difPeak * weightpeak; + newleft = prevpeak + difPeak * weightpeak; + } + } + + if (numHits < nGausForFit - 1) { + if ((peakMean + nsigmaADC * peakWidth) > (nextpeak - nsigmaADC * nextpeakSig)) { + float difPeak = nextpeak - peakMean; + float weightpeak = peakWidth / (nextpeakSig + peakWidth); + HitsumEndItr = range.begin() + peakMean + difPeak * weightpeak; + newright = peakMean + difPeak * weightpeak; + } + } + } + + //protection to avoid negative ranges + if (newright - newleft < 0) continue; + if (HitsumStartItr > HitsumEndItr) continue; + + //avoid ranges out of ROI if it happens + if (HitsumStartItr < sumStartItr) HitsumStartItr = sumStartItr; + + if (HitsumEndItr > sumEndItr) HitsumEndItr = sumEndItr; + + // ### Sum of ADC counts + float ROIsumADC = std::accumulate(sumStartItr, sumEndItr, 0.); + float HitsumADC = std::accumulate(HitsumStartItr, HitsumEndItr, 0.); + float peakTime = peakMean + roiFirstBinTick; + float chi2ByNDF = chi2PerNDF; // convert to float + + // ok, now create the hit + sbn::HitCreator hitcreator( + *channelROI, // channelROI reference + wid, // wire ID + startT + roiFirstBinTick, // start_tick TODO check + endT + roiFirstBinTick, // end_tick TODO check + peakWidth, // rms + peakTime, // peak_time + peakMeanErr, // sigma_peak_time + peakAmp, // peak_amplitude + peakAmpErr, // sigma_peak_amplitude + charge, // hit_integral + chargeErr, // hit_sigma_integral + ROIsumADC, // summedADC of the ROI + HitsumADC, // summedADC of the Hit + nGausForFit, // multiplicity + numHits, // local_index TODO check that the order is correct + chi2ByNDF, // goodness_of_fit + NDF // dof + ); + + if (filteredHitCol) filteredHitVec.push_back(hitcreator.copy()); + + const recob::Hit hit(hitcreator.move()); + + // This loop will store ALL hits + hitstruct tmp{std::move(hit), channelROI}; + hitstruct_vec.push_back(std::move(tmp)); + + numHits++; + } // <---End loop over gaussians + + // Should we filter hits? + if (filteredHitCol && !filteredHitVec.empty()) { + // ####################################################################### + // Is all this sorting really necessary? Would it be faster to just loop + // through the hits and perform simple cuts on amplitude and width on a + // hit-by-hit basis, either here in the module (using fPulseHeightCuts and + // fPulseWidthCuts) or in HitFilterAlg? + // ####################################################################### + + // Sort in ascending peak height + std::sort(filteredHitVec.begin(), + filteredHitVec.end(), + [](const auto& left, const auto& right) { + return left.PeakAmplitude() > right.PeakAmplitude(); + }); + + // Reject if the first hit fails the PH/wid cuts + if (filteredHitVec.front().PeakAmplitude() < fPulseHeightCuts.at(plane) || + filteredHitVec.front().RMS() < fPulseWidthCuts.at(plane)) + filteredHitVec.clear(); + + // Now check other hits in the snippet + if (filteredHitVec.size() > 1) { + // The largest pulse height will now be at the front... + float largestPH = filteredHitVec.front().PeakAmplitude(); + + // Find where the pulse heights drop below threshold + float threshold(fPulseRatioCuts.at(plane)); + + std::vector::iterator smallHitItr = + std::find_if(filteredHitVec.begin(), + filteredHitVec.end(), + [largestPH, threshold](const auto& hit) { + return hit.PeakAmplitude() < 8. && + hit.PeakAmplitude() / largestPH < threshold; + }); + + // Shrink to fit + if (smallHitItr != filteredHitVec.end()) + filteredHitVec.resize(std::distance(filteredHitVec.begin(), smallHitItr)); + + // Resort in time order + std::sort(filteredHitVec.begin(), + filteredHitVec.end(), + [](const auto& left, const auto& right) { + return left.PeakTime() < right.PeakTime(); + }); + } + + // Copy the hits we want to keep to the filtered hit collection +// for (const auto& filteredHit : filteredHitVec) +// if (!fHitFilterAlg || fHitFilterAlg->IsGoodHit(filteredHit)) { +// hitstruct tmp{std::move(filteredHit), channelROI}; +// filthitstruct_vec.push_back(std::move(tmp)); +// } +// + if (fFillHists) fChi2->Fill(chi2PerNDF); + } + } //<---End loop over merged candidate hits + } //<---End looping over ROI's + ); //end tbb parallel for + } //<---End looping over all the wires + ); //end tbb parallel for + + for (size_t i = 0; i < hitstruct_vec.size(); i++) { + allHitCol.emplace_back(hitstruct_vec[i].hit_tbb, hitstruct_vec[i].channelROI_tbb); + } + + for (size_t j = 0; j < filthitstruct_vec.size(); j++) { + filteredHitCol->emplace_back(filthitstruct_vec[j].hit_tbb, filthitstruct_vec[j].channelROI_tbb); + } + + //================================================================================================== + // End of the event -- move the hit collection and the associations into the event + + if (filteredHitCol) { + + // If we filtered hits but no instance name was + // specified for the "all hits" collection, then + // only save the filtered hits to the event + if (fAllHitsInstanceName == "") { + filteredHitCol->put_into(evt); + + // otherwise, save both + } + else { + filteredHitCol->put_into(evt); + allHitCol.put_into(evt); + } + } + else { + allHitCol.put_into(evt); + } + } // End of produce() + + DEFINE_ART_MODULE(GaussHitFinderSBN) + +} // end of hit namespace diff --git a/sbncode/HitFinder/HitFinderUtilities/CMakeLists.txt b/sbncode/HitFinder/HitFinderUtilities/CMakeLists.txt new file mode 100644 index 000000000..2e9266b58 --- /dev/null +++ b/sbncode/HitFinder/HitFinderUtilities/CMakeLists.txt @@ -0,0 +1,21 @@ +cet_make_library( + SOURCE + HitCreator.cxx + LIBRARIES + cetlib_except::cetlib_except + messagefacility::MF_MessageLogger + sbnobj::ICARUS_TPC + larcore::Geometry_Geometry_service + larcorealg::Geometry + lardata::ArtDataHelper + lardataobj::AnalysisBase + art::Framework_Core + art::Framework_Principal + art::Framework_Services_Registry + art::Persistency_Common + art::Persistency_Provenance + art::Utilities + ) + +install_headers() +install_source() diff --git a/sbncode/HitFinder/HitFinderUtilities/HitCreator.cxx b/sbncode/HitFinder/HitFinderUtilities/HitCreator.cxx new file mode 100644 index 000000000..76f9833ae --- /dev/null +++ b/sbncode/HitFinder/HitFinderUtilities/HitCreator.cxx @@ -0,0 +1,638 @@ +/** **************************************************************************** + * @file HitCreator.cxx + * @brief Helper functions to create a hit from ChannelROIs - implementation file + * @date April 20, 2025 + * @author usher@slac.stanford.edu + * @see Hit.h HitCreator.h + * + * ****************************************************************************/ + +// declaration header +#include "sbncode/HitFinder/HitFinderUtilities/HitCreator.h" + +// C/C++ standard library +#include // std::accumulate(), std::max() +#include +#include // std::numeric_limits<> +#include // std::move() + +// art libraries +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "canvas/Persistency/Common/FindOneP.h" +#include "canvas/Utilities/Exception.h" + +// LArSoft libraries +#include "larcore/Geometry/WireReadout.h" +#include "lardata/Utilities/MakeIndex.h" + +namespace { + + /// Erases the content of an association + template + void ClearAssociations(art::Assns& assns) + { + art::Assns empty; + assns.swap(empty); + } // ClearAssociations() + +} // local namespace + +/// Reconstruction base classes +namespace sbn { + + //**************************************************************************** + //*** HitCreator + //---------------------------------------------------------------------- + HitCreator::HitCreator(raw::RawDigit const& digits, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof) + : hit(digits.Channel(), + start_tick, + end_tick, + peak_time, + sigma_peak_time, + rms, + peak_amplitude, + sigma_peak_amplitude, + ROIsummedADC, + HitsummedADC, + hit_integral, + hit_sigma_integral, + multiplicity, + local_index, + goodness_of_fit, + dof, + art::ServiceHandle()->Get().View(digits.Channel()), + art::ServiceHandle()->Get().SignalType(digits.Channel()), + wireID) + {} // HitCreator::HitCreator(RawDigit) + + //---------------------------------------------------------------------- + HitCreator::HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof) + : hit(wire.Channel(), + start_tick, + end_tick, + peak_time, + sigma_peak_time, + rms, + peak_amplitude, + sigma_peak_amplitude, + ROIsummedADC, + HitsummedADC, + hit_integral, + hit_sigma_integral, + multiplicity, + local_index, + goodness_of_fit, + dof, + art::ServiceHandle()->Get().View(wire.Channel()), + art::ServiceHandle()->Get().SignalType(wire.Channel()), + wireID) + {} // HitCreator::HitCreator(Wire) + + //---------------------------------------------------------------------- + HitCreator::HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof) + : HitCreator(wire, + wireID, + start_tick, + end_tick, + rms, + peak_time, + sigma_peak_time, + peak_amplitude, + sigma_peak_amplitude, + hit_integral, + hit_sigma_integral, + std::accumulate(wire.SignalROI().begin() + start_tick, + wire.SignalROI().begin() + end_tick, + 0.), // sum of ADC counts between start_tick and end_tic + std::accumulate( + wire.SignalROI().begin() + start_tick, + wire.SignalROI().begin() + end_tick, + 0.), // sum of ADC counts between start_tick and end_tick TO BE MODIFIED IF HIT HAS TO BE CONSIDERED + multiplicity, + local_index, + goodness_of_fit, + dof) + {} // HitCreator::HitCreator(Wire; no summed ADC) + + //---------------------------------------------------------------------- + HitCreator::HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof, + RegionOfInterest_t const& signal) + : HitCreator(wire, + wireID, + signal.begin_index(), + signal.end_index(), + rms, + peak_time, + sigma_peak_time, + peak_amplitude, + sigma_peak_amplitude, + hit_integral, + hit_sigma_integral, + ROIsummedADC, + HitsummedADC, + multiplicity, + local_index, + goodness_of_fit, + dof) + {} // HitCreator::HitCreator(Wire; RoI) + + //---------------------------------------------------------------------- + HitCreator::HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof, + size_t iSignalRoI) + : HitCreator(wire, + wireID, + rms, + peak_time, + sigma_peak_time, + peak_amplitude, + sigma_peak_amplitude, + hit_integral, + hit_sigma_integral, + ROIsummedADC, + HitsummedADC, + multiplicity, + local_index, + goodness_of_fit, + dof, + wire.SignalROI().range(iSignalRoI)) + {} // HitCreator::HitCreator(Wire; RoI index) + + HitCreator::HitCreator(recob::Hit const& from) : hit(from) {} + + // Need to do this way since we don't have friendship for the wireID + HitCreator::HitCreator(recob::Hit const& from, geo::WireID const& wireID) + : hit(from.Channel(), + from.StartTick(), + from.EndTick(), + from.PeakTime(), + from.SigmaPeakTime(), + from.RMS(), + from.PeakAmplitude(), + from.SigmaPeakAmplitude(), + from.ROISummedADC(), + from.HitSummedADC(), + from.Integral(), + from.SigmaIntegral(), + from.Multiplicity(), + from.LocalIndex(), + from.GoodnessOfFit(), + from.DegreesOfFreedom(), + art::ServiceHandle()->Get().View(from.Channel()), + art::ServiceHandle()->Get().SignalType(from.Channel()), + wireID) + { } // HitCreator::HitCreator(new wire ID) + + //**************************************************************************** + //*** HitAndAssociationsWriterBase + //---------------------------------------------------------------------- + HitAndAssociationsWriterBase::HitAndAssociationsWriterBase(art::Event& event, + std::string instance_name, + bool doWireAssns, + bool doRawDigitAssns) + : prod_instance(instance_name) + , hits() + , WireAssns(doWireAssns ? new art::Assns : nullptr) + , RawDigitAssns(doRawDigitAssns ? new art::Assns : nullptr) + , event(&event) + , hitPtrMaker(*(this->event), prod_instance) + {} // HitAndAssociationsWriterBase::HitAndAssociationsWriterBase() + + //------------------------------------------------------------------------------ + void HitAndAssociationsWriterBase::declare_products(art::ProducesCollector& collector, + std::string instance_name /* = "" */, + bool doWireAssns /* = true */, + bool doRawDigitAssns /* = true */ + ) + { + collector.produces>(instance_name); + + // declare the other products we are creating (if any) + if (doWireAssns) { collector.produces>(instance_name); } + if (doRawDigitAssns) { + collector.produces>(instance_name); + } + } // HitAndAssociationsWriterBase::declare_products() + + //------------------------------------------------------------------------------ + void HitAndAssociationsWriterBase::put_into() + { + assert(event); + if (hits) event->put(std::move(hits), prod_instance); + if (WireAssns) event->put(std::move(WireAssns), prod_instance); + if (RawDigitAssns) event->put(std::move(RawDigitAssns), prod_instance); + } // HitAndAssociationsWriterBase::put_into() + + //**************************************************************************** + //*** HitCollectionCreator + //---------------------------------------------------------------------- + HitCollectionCreator::HitCollectionCreator(art::Event& event, + std::string instance_name /* = "" */, + bool doWireAssns /* = true */, + bool doRawDigitAssns /* = true */ + ) + : HitAndAssociationsWriterBase(event, instance_name, doWireAssns, doRawDigitAssns) + { + hits.reset(new std::vector); + } // HitCollectionCreator::HitCollectionCreator() + + //---------------------------------------------------------------------- + void HitCollectionCreator::emplace_back(recob::Hit&& hit, + art::Ptr const& wire, + art::Ptr const& digits) + { + + // add the hit to the collection + hits->emplace_back(std::move(hit)); + + CreateAssociationsToLastHit(wire, digits); + } // HitCollectionCreator::emplace_back(Hit&&) + + //---------------------------------------------------------------------- + void HitCollectionCreator::emplace_back(recob::Hit const& hit, + art::Ptr const& wire, + art::Ptr const& digits) + { + + // add the hit to the collection + hits->push_back(hit); + + CreateAssociationsToLastHit(wire, digits); + } // HitCollectionCreator::emplace_back(Hit) + + //---------------------------------------------------------------------- + void HitCollectionCreator::put_into() + { + if (!hits) { + throw art::Exception(art::errors::LogicError) + << "HitCollectionCreator is trying to put into the event" + " a hit collection that was never created!\n"; + } + HitAndAssociationsWriterBase::put_into(); + } // HitCollectionCreator::put_into() + + //---------------------------------------------------------------------- + void HitCollectionCreator::CreateAssociationsToLastHit(art::Ptr const& wire, + art::Ptr const& digits) + { + // if no association is required, we are done + if (!WireAssns && !RawDigitAssns) return; + + // art pointer to the hit we just created + HitPtr_t hit_ptr(CreatePtrToLastHit()); + + // association with wires + if (WireAssns && wire.isNonnull()) + WireAssns->addSingle(wire, hit_ptr); // if it fails, it throws + + // association with wires + if (RawDigitAssns && digits.isNonnull()) + RawDigitAssns->addSingle(digits, hit_ptr); // if it fails, it throws + + } // HitCollectionCreator::CreateAssociationsToLastHit() + + //**************************************************************************** + //*** HitCollectionAssociator + //---------------------------------------------------------------------- + HitCollectionAssociator::HitCollectionAssociator(art::Event& event, + std::string instance_name, + art::InputTag const& WireModuleLabel, + art::InputTag const& RawDigitModuleLabel) + : HitAndAssociationsWriterBase(event, + instance_name, + WireModuleLabel != "", + RawDigitModuleLabel != "") + , wires_label(WireModuleLabel) + , digits_label(RawDigitModuleLabel) + { + hits.reset(new std::vector); + } // HitCollectionAssociator::HitCollectionAssociator() + + //---------------------------------------------------------------------- + sbn::HitCollectionAssociator::HitCollectionAssociator(art::Event& event, + std::string instance_name, + art::InputTag const& WireModuleLabel, + bool doRawDigitAssns /* = false */ + ) + : HitAndAssociationsWriterBase(event, instance_name, WireModuleLabel != "", doRawDigitAssns) + , wires_label(WireModuleLabel) + , digits_label() + { + if (RawDigitAssns && !WireAssns) { + throw art::Exception(art::errors::LogicError) + << "HitCollectionAssociator can't create hit <--> raw digit" + " associations through wires, without wires!\n"; + } + hits.reset(new std::vector); + } // HitCollectionAssociator::HitCollectionAssociator() + + //---------------------------------------------------------------------- + void HitCollectionAssociator::use_hits(std::unique_ptr>&& srchits) + { + hits = std::move(srchits); + } // HitCollectionAssociator::use_hits() + + //---------------------------------------------------------------------- + void HitCollectionAssociator::put_into() + { + prepare_associations(); + HitAndAssociationsWriterBase::put_into(); + } // HitCollectionAssociator::put_into() + + //---------------------------------------------------------------------- + void HitCollectionAssociator::prepare_associations(std::vector const& srchits) + { + if (!RawDigitAssns && !WireAssns) return; // no associations needed + assert(event); + + // we make the associations anew + if (RawDigitAssns) ClearAssociations(*RawDigitAssns); + if (WireAssns) ClearAssociations(*WireAssns); + + // the following is true is we want associations with digits + // but we don't know where digits are; in that case, we try to use wires + const bool bUseWiresForDigits = RawDigitAssns && (digits_label == ""); + + if (WireAssns || bUseWiresForDigits) { + // do we use wires for digit associations too? + + // get the wire collection + art::ValidHandle> hWires = + event->getValidHandle>(wires_label); + + // fill a map of wire index vs. channel number + std::vector WireMap = util::MakeIndex(*hWires, std::mem_fn(&recob::ChannelROI::Channel)); + + // use raw rigit - wire association, assuming they have been produced + // by the same producer as the wire and with the same instance name; + // we don't check whether the data product is found, but the following + // code will have FindOneP throw if that was not the case + // (that's what we would do here anyway, maybe with a better message...) + std::unique_ptr> WireToDigit; + if (bUseWiresForDigits) { + WireToDigit.reset(new art::FindOneP(hWires, *event, wires_label)); + } + + // add associations, hit by hit: + for (size_t iHit = 0; iHit < srchits.size(); ++iHit) { + + // find the channel + size_t iChannel = size_t(srchits[iHit].Channel()); // forcibly converted + + // find the wire associated to that channel + size_t iWire = std::numeric_limits::max(); + if (iChannel < WireMap.size()) iWire = WireMap[iChannel]; + if (iWire == std::numeric_limits::max()) { + throw art::Exception(art::errors::LogicError) + << "No wire associated to channel #" << iChannel << " whence hit #" << iHit + << " comes!\n"; + } // if no channel + + // make the association with wires + if (WireAssns) { + art::Ptr wire(hWires, iWire); + WireAssns->addSingle(wire, CreatePtr(iHit)); + } + + if (bUseWiresForDigits) { + // find the digit associated to that channel + art::Ptr const& digit = WireToDigit->at(iWire); + if (digit.isNull()) { + throw art::Exception(art::errors::LogicError) + << "No raw digit associated to channel #" << iChannel << " whence hit #" << iHit + << " comes!\n"; + } // if no channel + + // make the association + RawDigitAssns->addSingle(digit, CreatePtr(iHit)); + } // if create digit associations through wires + } // for hit + + } // if wire associations + + if (RawDigitAssns && !bUseWiresForDigits) { + // get the digit collection + art::ValidHandle> hDigits = + event->getValidHandle>(digits_label); + + // fill a map of wire index vs. channel number + std::vector DigitMap = + util::MakeIndex(*hDigits, std::mem_fn(&raw::RawDigit::Channel)); + + // add associations, hit by hit: + for (size_t iHit = 0; iHit < srchits.size(); ++iHit) { + + // find the channel + size_t iChannel = size_t(srchits[iHit].Channel()); // forcibly converted + + // find the digit associated to that channel + size_t iDigit = std::numeric_limits::max(); + if (iChannel < DigitMap.size()) iDigit = DigitMap[iChannel]; + if (iDigit == std::numeric_limits::max()) { + throw art::Exception(art::errors::LogicError) + << "No raw digit associated to channel #" << iChannel << " whence hit #" << iHit + << " comes!\n"; + } // if no channel + + // make the association + art::Ptr digit(hDigits, iDigit); + RawDigitAssns->addSingle(digit, CreatePtr(iHit)); + + } // for hit + } // if we have rawdigit label + + } // HitCollectionAssociator::put_into() + + //**************************************************************************** + //*** HitRefinerAssociator + //---------------------------------------------------------------------- + HitRefinerAssociator::HitRefinerAssociator(art::Event& event, + art::InputTag const& HitModuleLabel, + std::string instance_name /* = "" */, + bool doWireAssns /* = true */, + bool doRawDigitAssns /* = true */ + ) + : HitAndAssociationsWriterBase(event, instance_name, doWireAssns, doRawDigitAssns) + , hits_label(HitModuleLabel) + { + hits.reset(new std::vector); + } // HitRefinerAssociator::HitRefinerAssociator() + + //---------------------------------------------------------------------- + void HitRefinerAssociator::use_hits(std::unique_ptr>&& srchits) + { + hits = std::move(srchits); + } // HitRefinerAssociator::use_hits() + + //---------------------------------------------------------------------- + void HitRefinerAssociator::put_into() + { + prepare_associations(); + HitAndAssociationsWriterBase::put_into(); + } // HitRefinerAssociator::put_into() + + //---------------------------------------------------------------------- + void HitRefinerAssociator::prepare_associations(std::vector const& srchits) + { + if (!RawDigitAssns && !WireAssns) return; // no associations needed + assert(event); + + // we make the associations anew + if (RawDigitAssns) ClearAssociations(*RawDigitAssns); + + // read the hits; this is going to hurt performances... + // no solution to that until there is a way to have a lazy read + art::ValidHandle> hHits = + event->getValidHandle>(hits_label); + + // now get the associations + if (WireAssns) { + // we make the associations anew + ClearAssociations(*WireAssns); + + // find the associations between the hits and the wires + art::FindOneP HitToWire(hHits, *event, hits_label); + if (!HitToWire.isValid()) { + throw art::Exception(art::errors::ProductNotFound) + << "Can't find the associations between hits and wires produced by '" << hits_label + << "'!\n"; + } // if no association + + // fill a map of wire vs. channel number + std::vector> WireMap; + for (size_t iAssn = 0; iAssn < HitToWire.size(); ++iAssn) { + art::Ptr wire = HitToWire.at(iAssn); + if (wire.isNull()) continue; + size_t channelID = (size_t)wire->Channel(); + if (WireMap.size() <= channelID) // expand the map of necessary + WireMap.resize(std::max(channelID + 1, 2 * WireMap.size()), {}); + WireMap[channelID] = std::move(wire); + } // for + + // now go through all the hits... + for (size_t iHit = 0; iHit < srchits.size(); ++iHit) { + recob::Hit const& hit = srchits[iHit]; + size_t channelID = (size_t)hit.Channel(); + + // no association if there is no wire to associate with + if ((channelID >= WireMap.size()) || !WireMap[channelID]) continue; + + // create an association using the same wire pointer + WireAssns->addSingle(WireMap[channelID], CreatePtr(iHit)); + } // for hits + } // if wire associations + + // now get the associations + if (RawDigitAssns) { + // we make the associations anew + ClearAssociations(*RawDigitAssns); + + // find the associations between the hits and the raw digits + art::FindOneP HitToDigits(hHits, *event, hits_label); + if (!HitToDigits.isValid()) { + throw art::Exception(art::errors::ProductNotFound) + << "Can't find the associations between hits and raw digits" + << " produced by '" << hits_label << "'!\n"; + } // if no association + + // fill a map of digits vs. channel number + std::vector> DigitMap; + for (size_t iAssn = 0; iAssn < HitToDigits.size(); ++iAssn) { + art::Ptr digits = HitToDigits.at(iAssn); + if (digits.isNull()) continue; + size_t channelID = (size_t)digits->Channel(); + if (DigitMap.size() <= channelID) // expand the map of necessary + DigitMap.resize(std::max(channelID + 1, 2 * DigitMap.size()), {}); + DigitMap[channelID] = std::move(digits); + } // for + + // now go through all the hits... + for (size_t iHit = 0; iHit < srchits.size(); ++iHit) { + recob::Hit const& hit = srchits[iHit]; + size_t channelID = (size_t)hit.Channel(); + + // no association if there is no digits to associate with + if ((channelID >= DigitMap.size()) || !DigitMap[channelID]) continue; + + // create an association using the same digits pointer + RawDigitAssns->addSingle(DigitMap[channelID], CreatePtr(iHit)); + } // for hits + } // if digit associations + + } // HitRefinerAssociator::put_into() + +} // namespace recob diff --git a/sbncode/HitFinder/HitFinderUtilities/HitCreator.h b/sbncode/HitFinder/HitFinderUtilities/HitCreator.h new file mode 100644 index 000000000..658fc1a9b --- /dev/null +++ b/sbncode/HitFinder/HitFinderUtilities/HitCreator.h @@ -0,0 +1,1062 @@ +/** **************************************************************************** + * @file HitCreator.h + * @brief Helper functions to create a hit from ChannelROIs + * @date April 20, 2025 + * @author Tracy Usher (usher@slac.stanford.edu) + * @see Hit.h HitCreator.cxx + * + * ****************************************************************************/ + +#ifndef SBN_ARTDATAHELPERS_HITCREATOR_H +#define SBN_ARTDATAHELPERS_HITCREATOR_H + +// LArSoft libraries +#include "lardataobj/RawData/RawDigit.h" +#include "lardataobj/RecoBase/Hit.h" +#include "sbnobj/ICARUS/TPC/ChannelROI.h" + +// framework libraries +#include "art/Framework/Core/ProducesCollector.h" +#include "art/Persistency/Common/PtrMaker.h" +#include "canvas/Persistency/Common/Assns.h" +#include "canvas/Persistency/Common/Ptr.h" +#include "canvas/Utilities/Exception.h" +#include "canvas/Utilities/InputTag.h" + +// C/C++ standard library +#include +#include // std::move() +#include + +namespace geo { + struct WireID; +} +namespace raw { + class RawDigit; +} +namespace art { + class ProducesCollector; + class Event; +} + +/// Reconstruction base classes +namespace sbn { + + /** ************************************************************************** + * @brief Class managing the creation of a new `recob::Hit` object. + * + * In order to be as simple as possible (Plain Old Data), data products like + * `recob::Hit` need to be stripped of most of their functions, including the + * ability to communicate whether a value we try to store is invalid + * (that would require a art::Exception` -- art -- or at least a message on + * the screen -- MessageFacility) and the ability to read things from event, + * services (e.g. geometry) etc. + * + * A Creator is a class that creates a temporary data product, and at the + * end it yields it to the caller for storage. + * This last step should be by move construction, although a copy method is + * also provided. + * + * An example of creating a `recob::Hit` object (assuming all the relevant + * variables have been assigned proper values): + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * recob::HitCreator hit( + * wire, wireID, + * start_tick, end_tick, rms, + * peak_time, sigma_peak_time, peak_amplitude, sigma_peak_amplitude, + * hit_integral, hit_sigma_integral, summedADC, + * multiplicity, local_index, goodness_of_fit, dof + * ); + * hit.push_back(hit.move()); // hit content is not valid any more + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This is a one-step creation object: the hit is constructed at the same + * time the HitCreator is, and no facility is offered to modify the + * constructed hit, or to create another one. + * + * The constructors currently provided are: + * 1. from RawDigit (extracts channel, view and signal type [CVS] thanks to + * geometry) + * 2. from `recob::ChannelROI`, [CVS] + * 3. from `recob::ChannelROI`, [CVS], `summedADC` is automatically computed from + * wire + * 4. from `recob::ChannelROI`, [CVS], start and stop time from a region of interest + * 5. from `recob::ChannelROI`, [CVS], start and stop time from index of region of + * interest + */ + class HitCreator { + public: + /// Type of one region of interest. + using RegionOfInterest_t = recob::ChannelROI::RegionsOfInterest_f::datarange_t; + + // destructor, copy and move constructor and assignment as default + + /** + * @brief Constructor: extracts some information from raw digit. + * @param digits a pointer to a `raw::RawDigit` (for channel, view, signal + * type) + * @param wireID ID of the wire the hit is on + * @param start_tick first tick in the region the hit was extracted from + * @param end_tick first tick after the region the hit was extracted from + * @param rms RMS of the signal hit, in TDC time units + * @param peak_time time at peak of the signal, in TDC time units + * @param sigma_peak_time uncertainty on time at peak, in TDC time units + * @param peak_amplitude amplitude of the signal at peak, in ADC units + * @param sigma_peak_amplitude uncertainty on amplitude at peak + * @param hit_integral total charge integrated under the hit signal + * @param hit_sigma_integral uncertainty on the total hit charge + * @param ROIsummedADC total ADC count in the region assigned to the hit ROI + * @param HitsummedADC total ADC count in the region assigned to the hit HIT + * @param multiplicity number of hits in the region it was extracted from + * @param local_index index of this hit in the region it was extracted + * from + * @param goodness_of_fit quality parameter for the hit + * @param dof degrees of freedom in the definition of the hit shape + * + * The information used from the raw digit is the channel ID; view and + * signal type are obtained from geometry. + */ + HitCreator(raw::RawDigit const& digits, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof); + + /** + * @brief Constructor: extracts some information from wire. + * @param wire a pointer to a `recob::ChannelROI` (for channel, view, signal + * type) + * @param wireID ID of the wire the hit is on + * @param start_tick first tick in the region the hit was extracted from + * @param end_tick first tick after the region the hit was extracted from + * @param rms RMS of the signal hit, in TDC time units + * @param peak_time time at peak of the signal, in TDC time units + * @param sigma_peak_time uncertainty on time at peak, in TDC time units + * @param peak_amplitude amplitude of the signal at peak, in ADC units + * @param sigma_peak_amplitude uncertainty on amplitude at peak + * @param hit_integral total charge integrated under the hit signal + * @param hit_sigma_integral uncertainty on the total hit charge + * @param ROIsummedADC total ADC count in the region assigned to the hit ROI + * @param HitsummedADC total ADC count in the region assigned to the hit Hit + * @param multiplicity number of hits in the region it was extracted from + * @param local_index index of this hit in the region it was extracted + * from + * @param goodness_of_fit quality parameter for the hit + * @param dof degrees of freedom in the definition of the hit shape + * + * The information used from the wire are the channel ID and view; + * the signal type is obtained from geometry. + */ + HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof); + + /** + * @brief Constructor: computes sum of ADC from wire. + * @param wire a pointer to a `recob::ChannelROI` (for channel, view, signal + * type) + * @param wireID ID of the wire the hit is on + * @param start_tick first tick in the region the hit was extracted from + * @param end_tick first tick after the region the hit was extracted from + * @param rms RMS of the signal hit, in TDC time units + * @param peak_time time at peak of the signal, in TDC time units + * @param sigma_peak_time uncertainty on time at peak, in TDC time units + * @param peak_amplitude amplitude of the signal at peak, in ADC units + * @param sigma_peak_amplitude uncertainty on amplitude at peak + * @param hit_integral total charge integrated under the hit signal + * @param hit_sigma_integral uncertainty on the total hit charge + * @param multiplicity number of hits in the region it was extracted from + * @param local_index index of this hit in the region it was extracted from + * @param goodness_of_fit quality parameter for the hit + * @param dof degrees of freedom in the definition of the hit shape + * + * The information used from the wire are the channel ID, view; + * the signal type is obtained from geometry. + * + * The sum of ADC counts is automatically computed over the whole range + * of the wire signal between `start_tick` and `end_tick` + * (the latter excluded). + */ + HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + raw::TDCtick_t start_tick, + raw::TDCtick_t end_tick, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof); + + /** + * @brief Constructor: uses region of interest specified by index. + * @param wire a pointer to a `recob::ChannelROI` (for channel, view, signal + * type) + * @param wireID ID of the wire the hit is on + * @param rms RMS of the signal hit, in TDC time units + * @param peak_time time at peak of the signal, in TDC time units + * @param sigma_peak_time uncertainty on time at peak, in TDC time units + * @param peak_amplitude amplitude of the signal at peak, in ADC units + * @param sigma_peak_amplitude uncertainty on amplitude at peak + * @param hit_integral total charge integrated under the hit signal + * @param hit_sigma_integral uncertainty on the total hit charge + * @param ROIsummedADC total ADC count in the region assigned to the hit ROI + * @param HitsummedADC total ADC count in the region assigned to the hit Hit + * @param multiplicity number of hits in the region it was extracted from + * @param local_index index of this hit in the region it was extracted + * from + * @param goodness_of_fit quality parameter for the hit + * @param dof degrees of freedom in the definition of the hit shape + * @param signal the signal region the hit was extracted from + * + * The information used from the wire are the channel ID, view + * and the region of interest; the signal type is obtained from + * geometry. + * + * Signal start and end ticks are extracted from the region of interest. + */ + HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof, + RegionOfInterest_t const& signal); + + /** + * @brief Constructor: uses region of interest specified by index. + * @param wire a pointer to a `recob::ChannelROI` (for channel, view, signal + * type) + * @param wireID ID of the wire the hit is on + * @param rms RMS of the signal hit, in TDC time units + * @param peak_time time at peak of the signal, in TDC time units + * @param sigma_peak_time uncertainty on time at peak, in TDC time units + * @param peak_amplitude amplitude of the signal at peak, in ADC units + * @param sigma_peak_amplitude uncertainty on amplitude at peak + * @param hit_integral total charge integrated under the hit signal + * @param hit_sigma_integral uncertainty on the total hit charge + * @param ROI summedADC total ADC count in the region assigned to the hit ROI + * @param Hit summedADC total ADC count in the region assigned to the hit Hit + * @param multiplicity number of hits in the region it was extracted from + * @param local_index index of this hit in the region it was extracted from + * @param goodness_of_fit quality parameter for the hit + * @param dof degrees of freedom in the definition of the hit shape + * @param iSignalRoI index in the wire of the signal region the hit was + * extracted from + * + * The information used from the wire are the channel ID, view + * and the region of interest; the signal type is obtained from + * geometry. + * + * Signal start and end ticks are extracted from the region of interest. + */ + HitCreator(recob::ChannelROI const& wire, + geo::WireID const& wireID, + float rms, + float peak_time, + float sigma_peak_time, + float peak_amplitude, + float sigma_peak_amplitude, + float hit_integral, + float hit_sigma_integral, + float ROIsummedADC, + float HitsummedADC, + short int multiplicity, + short int local_index, + float goodness_of_fit, + int dof, + size_t iSignalRoI); + + /** + * @brief Constructor: copies from an existing hit. + * @param from the original hit + */ + HitCreator(recob::Hit const& from); + + /** + * @brief Constructor: copies from an existing hit, changing wire ID. + * @param from the original hit + * @param wireID ID of the new wire the hit is on + */ + HitCreator(recob::Hit const& from, geo::WireID const& wireID); + + /** + * @brief Prepares the constructed hit to be moved away. + * @return a right-value reference to the constructed hit + * + * Despite the name, no move happens in this function. + * Move takes place in the caller code as proper; for example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * // be hit a HitCreator instance: + * std::vector Hits; + * hit.move(); // nothing happens + * Hits.push_back(hit.move()); // here the copy happens + * recob::Hit single_hit(hit.move()); // wrong! hit is empty now + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + recob::Hit&& move() { return std::move(hit); } + + /** + * @brief Returns the constructed wire + * @return a constant reference to the constructed wire + * + * Despite the name, no copy happens in this function. + * Copy takes place in the caller code as proper; for example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * // be Hit a HitCreator instance: + * std::vector Hits; + * hit.copy(); // nothing happens + * Hits.push_back(hit.copy()); // here a copy happens + * recob::Hit single_hit(hit.copy()); // hit is copied again + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + recob::Hit const& copy() const { return hit; } + + protected: + recob::Hit hit; ///< Local instance of the hit being constructed. + + }; // class HitCreator + + /** ************************************************************************** + * @brief Base class handling a collection of hits and its associations. + * + * Instead of creating a collection of hits, one for its association with + * wires and one for its association with raw digits, one can use a class + * derived from this one: + * - `HitCollectionCreator`: push new hits one by one + * - `HitCollectionAssociator`: push a complete collection of hits + * - `HitRefinerAssociator`: push a complete collection of hits deriving their + * associations from other hits + * Using `put_into()` will transfer into the event the data. + * + * The typical usage is to have the constructor of the module call the static + * function + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * HitAndAssociationsWriterBase::declare_products(*this); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * (this example declares a collection with empty instance name and that we + * want associations to both wires and raw digits), and then in `produce()`: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * HitAndAssociationsWriterDerived hcol(*this, event); + * + * // ... fill hcol in the proper way ... + * + * hcol.put_into(); // calls art::Event::put() + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + class HitAndAssociationsWriterBase { + public: + // no public constructor: use one of the derived classes! + // destructor, copy and move constructors and assignment are default + + /// Returns the number of hits currently in the collection. + size_t size() const { return hits ? hits->size() : 0; } + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + * + * @deprecated Use the version with no arguments instead. + */ + void put_into(art::Event&) { put_into(); } + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + */ + void put_into(); + + /// Returns a read-only reference to the current list of hits. + std::vector const& peek() const { return *hits; } + + /** + * @brief Declares the hit products we are going to fill. + * @tparam ModuleType type of producing module (`EDProducer` or `EDFilter`) + * @param producer the module producing the data products + * @param instance_name name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * This declaration must be given in the constructor of producer. + * It is equivalent to manually declare the relevant among these products: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * produces>(prod_instance); + * produces>(prod_instance); + * produces>(prod_instance); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * in the producer constructor. + * All the data products (hit collection and associations) will have the + * specified product instance name. + */ + static void declare_products(art::ProducesCollector& collector, + std::string instance_name = "", + bool doWireAssns = true, + bool doRawDigitAssns = true); + + protected: + using HitPtr_t = art::Ptr; ///< Type of art pointer to Hit. + + std::string prod_instance; ///< Tame of the instance for data products. + + /// Collection of hits. + std::unique_ptr> hits; + /// Associations with wires. + std::unique_ptr> WireAssns; + /// Associations with raw digits. + std::unique_ptr> RawDigitAssns; + + art::Event* event = nullptr; ///< Pointer to the event we are using. + + art::PtrMaker hitPtrMaker; ///< Tool to create hit pointers, + + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param instance_name name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * All the data products (hit collection and associations) will have the + * specified product instance name. + */ + HitAndAssociationsWriterBase(art::Event& event, + std::string instance_name, + bool doWireAssns, + bool doRawDigitAssns); + + /// Creates an art pointer to the hit with the specified index. + HitPtr_t CreatePtr(size_t index) const { return hitPtrMaker(index); } + + }; // class HitAndAssociationsWriterBase + + /** ************************************************************************** + * @brief A class handling a collection of hits and its associations. + * + * Instead of creating a collection of hits, one for its association with + * wires and one for its association with raw digits, one can push hits into + * this object, and then move it into the event. + */ + class HitCollectionCreator : public HitAndAssociationsWriterBase { + public: + /// @name Constructors + /// @{ + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param instance_name name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * All the data products (hit collection and associations) will have the + * specified product instance name. + */ + HitCollectionCreator(art::Event& event, + std::string instance_name = "", + bool doWireAssns = true, + bool doRawDigitAssns = true); + + /** + * @brief Constructor: no product instance name. + * @param event the event the products are going to be put into + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + */ + HitCollectionCreator(art::Event& event, bool doWireAssns, bool doRawDigitAssns) + : HitCollectionCreator(event, "", doWireAssns, doRawDigitAssns) + {} + + /// @} + + // destructor, copy and move constructors and assignment are default + + /// @name Addition of hits + /// @{ + /** + * @brief Adds the specified hit to the data collection. + * @param hit the hit that will be moved into the collection + * @param wire art pointer to the wire to be associated to this hit + * @param digits art pointer to the raw digits to be associated to this hit + * + * After this call, hit will be invalid. + * If a art pointer is not valid, that association will not be stored. + */ + void emplace_back(recob::Hit&& hit, + art::Ptr const& wire = art::Ptr(), + art::Ptr const& digits = art::Ptr()); + + /** + * @brief Adds the specified hit to the data collection. + * @param hit the hit that will be copied into the collection + * @param wire art pointer to the wire to be associated to this hit + * @param digits art pointer to the raw digits to be associated to this hit + * + * If a art pointer is not valid, that association will not be stored. + */ + void emplace_back(recob::Hit const& hit, + art::Ptr const& wire = art::Ptr(), + art::Ptr const& digits = art::Ptr()); + + /** + * @brief Adds the specified hit to the data collection. + * @param hit the HitCreator object containing the hit + * @param wire art pointer to the wire to be associated to this hit + * @param digits art pointer to the raw digits to be associated to this hit + * + * After this call, the hit creator will be empty. + * If a art pointer is not valid, that association will not be stored. + */ + void emplace_back(HitCreator&& hit, + art::Ptr const& wire = art::Ptr(), + art::Ptr const& digits = art::Ptr()) + { + emplace_back(hit.move(), wire, digits); + } + + /** + * @brief Adds the specified hit to the data collection. + * @param hit the hit that will be moved into the collection + * @param digits art pointer to the raw digits to be associated to this hit + * + * After this call, hit will be invalid. + * If the digit pointer is not valid, its association will not be stored. + */ + void emplace_back(recob::Hit&& hit, art::Ptr const& digits) + { + emplace_back(std::move(hit), art::Ptr(), digits); + } + + /** + * @brief Adds the specified hit to the data collection. + * @param hit the HitCreator object containing the hit + * @param digits art pointer to the raw digits to be associated to this hit + * + * After this call, the hit creator will be empty. + * If the digit pointer is not valid, its association will not be stored. + */ + void emplace_back(HitCreator&& hit, art::Ptr const& digits) + { + emplace_back(std::move(hit), art::Ptr(), digits); + } + + /** + * @brief Adds the specified hit to the data collection. + * @param hit the HitCreator object containing the hit + * @param digits art pointer to the raw digits to be associated to this hit + * + * If the digit pointer is not valid, its association will not be stored. + */ + void emplace_back(HitCreator const& hit, art::Ptr const& digits) + { + emplace_back(std::move(hit.copy()), art::Ptr(), digits); + } + /// @} + + /// Returns the number of hits currently in the collection. + size_t size() const { return hits->size(); } + + /// Prepares the collection to host at least `new_size` hits. + void reserve(size_t new_size) + { + if (hits) hits->reserve(new_size); + } + + /** + * @brief Moves the data into an event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + * + * @deprecated Use the version with no arguments instead. + */ + void put_into(art::Event&) { put_into(); } + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + */ + void put_into(); + + /// Returns a read-only reference to the current list of hits. + std::vector const& peek() const { return *hits; } + + protected: + using HitPtr_t = HitAndAssociationsWriterBase::HitPtr_t; + + /// Creates an art pointer to the hit with the last index. + HitPtr_t CreatePtrToLastHit() const + { + return hits->empty() ? HitPtr_t() : CreatePtr(hits->size() - 1); + } + + /// Creates associations between the last hit and the specified pointers. + void CreateAssociationsToLastHit(art::Ptr const& wire, + art::Ptr const& digits); + + }; // class HitCollectionCreator + + /** ************************************************************************** + * @brief A class handling a collection of hits and its associations. + * + * Use this object if you already have a collection of `recob::Hit` and you + * simply want the hits associated to the wire and digit with the same + * channel. + */ + class HitCollectionAssociator : public HitAndAssociationsWriterBase { + public: + /// @name Constructors + /// @{ + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param instance_name name of the instance for all data products + * @param WireModuleLabel label of the module used to create wires + * @param RawDigitModuleLabel label of the module used to create raw digits + * + * All the data products (hit collection and associations) will have the + * specified product instance name. + * + * If a label is empty, the corresponding association will not be produced. + */ + HitCollectionAssociator(art::Event& event, + std::string instance_name, + art::InputTag const& WireModuleLabel, + art::InputTag const& RawDigitModuleLabel); + + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param WireModuleLabel label of the module used to create wires + * @param RawDigitModuleLabel label of the module used to create raw digits + * + * All the data products (hit collection and associations) will have a + * default, empty product instance name. + * + * If a label is empty, the corresponding association will not be produced. + */ + HitCollectionAssociator(art::Event& event, + art::InputTag const& WireModuleLabel, + art::InputTag const& RawDigitModuleLabel) + : HitCollectionAssociator(event, "", WireModuleLabel, RawDigitModuleLabel) + {} + + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param instance_name name of the instance for all data products + * @param WireModuleLabel label of the module used to create wires + * @param doRawDigitAssns whether to write associations with raw digits + * + * All the data products (hit collection and associations) will have the + * specified product instance name. + * + * The raw digit association is built out of their existing associations + * with wires, rather than by directly using the raw digits data product. + */ + HitCollectionAssociator(art::Event& event, + std::string instance_name, + art::InputTag const& WireModuleLabel, + bool doRawDigitAssns); + + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param WireModuleLabel label of the module used to create wires + * @param doRawDigitAssns whether to write associations with raw digits + * + * All the data products (hit collection and associations) will have the + * default, empty product instance name. + * + * The raw digit association is built out of their existing associations + * with wires, rather than by directly using the raw digits data product. + */ + HitCollectionAssociator(art::Event& event, + art::InputTag const& WireModuleLabel, + bool doRawDigitAssns) + : HitCollectionAssociator(event, "", WireModuleLabel, doRawDigitAssns) + {} + + /// @} + + // destructor, copy and move constructors and assignment are default + + /** + * @brief Uses the specified collection as data product. + * @param srchits the collection to be used as data product + * + * The very same collection is put into the event. + * This object will temporary own the collection until the hits are put into + * the event. + * If there were previous hits in the object, they are lost. + */ + void use_hits(std::unique_ptr>&& srchits); + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + * + * @deprecated Use the version with no arguments instead. + */ + void put_into(art::Event&) { put_into(); } + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + */ + void put_into(); + + protected: + /// Label of the collection of wires to associate. + art::InputTag wires_label; + /// Label of raw digits collection to associate. + art::InputTag digits_label; + + /// Finds out the associations for the specified hits. + void prepare_associations(std::vector const& srchits); + + /// Finds out the associations for the current hits. + void prepare_associations() { prepare_associations(*hits); } + + }; // class HitCollectionAssociator + + /** ************************************************************************** + * @brief A class handling a collection of hits and its associations. + * + * Use this object if you already have a `recob::Hit` data product and + * another collection that is going to become a data product, and you + * simply want the new hits associated to the wire and digit with the same + * channel. + * No hit-to-hit association is attempted (that would be, incidentally, not + * supported by art): the data product is used to get all the associated + * wires and digits, then they are associated to the new hits by channel ID. + * If a channel is not available, a warning is produced. If different hits + * on the same channel are associated to different wires or raw digits, an + * exception is thrown. + */ + class HitRefinerAssociator : public HitAndAssociationsWriterBase { + public: + /// @name Constructors + /// @{ + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param HitModuleLabel label of the module used to create hits + * @param instance_name name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * All the data products (hit collection and associations) will have the + * specified product instance name. + */ + HitRefinerAssociator(art::Event& event, + art::InputTag const& HitModuleLabel, + std::string instance_name = "", + bool doWireAssns = true, + bool doRawDigitAssns = true); + + /** + * @brief Constructor: sets instance name and whether to build associations. + * @param event the event the products are going to be put into + * @param HitModuleLabel label of the module used to create hits + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * All the data products (hit collection and associations) will have an + * empty product instance name. + */ + HitRefinerAssociator(art::Event& event, + art::InputTag const& HitModuleLabel, + bool doWireAssns, + bool doRawDigitAssns = true) + : HitRefinerAssociator(event, HitModuleLabel, "", doWireAssns, doRawDigitAssns) + {} + + /// @} + + // destructor, copy and move constructors and assignment are default + + /** + * @brief Uses the specified collection as data product. + * @param srchits the collection to be used as data product + * + * The very same collection is put into the event. + * This object will temporary own the collection until the hits are put into + * the event. + * If there were previous hits in the object, they are lost. + */ + void use_hits(std::unique_ptr>&& srchits); + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + * + * @deprecated Use the version with no arguments instead. + * + */ + void put_into(art::Event&) { put_into(); } + + /** + * @brief Moves the data into the event. + * + * The calling module must have already declared the production of these + * products with the proper instance name. + * After the move, the collections in this object are empty. + */ + void put_into(); + + protected: + art::InputTag hits_label; ///< Label of the collection of hits. + + /// Finds out the associations for the specified hits. + void prepare_associations(std::vector const& srchits); + + /// Finds out the associations for the current hits. + void prepare_associations() { prepare_associations(*hits); } + + }; // class HitRefinerAssociator + + // --------------------------------------------------------------------------- + /** + * @brief A helper to centralise creation of a hit collection data product. + * @tparam Writer writer class to manage + * @tparam ModuleType owning module: `art::EDProducer` or `art::EDFilter` + * + * This class adds an indirection layer to the model relying on + * `HitAndAssociationsWriter`. In that one, two different steps are required, + * one in the constructor of the module, where data products are declared, and + * one in the place where hits are actually assembled. + * These two steps need consistent setup, but they are separate and + * formally independent. The "manager" approach consists of an object + * performing the first step directly, and delivering an already configured + * object for the second step. + * + * An example of usage in a module: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * class MyHitProducer: public art::EDProducer { + * + * recob::HitAndAssociationsWriterManager + * hitCollCreator; + * + * public: + * + * MyHitProducer(fhicl::ParameterSet const& pset) + * : EDProducer{pset} + * , hitCollCreator(*this, pset.get("instanceName", "")) + * {} + * + * void produce(art::Event& event) + * { + * auto hitCollWriter = hitCollCreator.collectionWriter(event); + * + * for (recob::ChannelROI const& wire: Wires) { + * // create hits... + * hitCollWriter.emplace_back(hit, wire, digit); + * } + * hitCollWriter.put_into(); + * } + * + * }; // class MyHitProducer + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + template + class HitAndAssociationsWriterManager { + + public: + using Writer_t = Writer; ///< Type of managed hit collection writer. + + /** + * @brief Constructor: does not declare anything. + * + * This constructor does not declare products. Calling `declare_products()` + * explicitly is then required in the module constructor. + * + */ + HitAndAssociationsWriterManager() = default; + + /** + * @brief Declares the hit products we are going to fill. + * @param collector the module this manager is bound to + * @param instanceName name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * This constructor calls `declareProducts()`. + */ + HitAndAssociationsWriterManager(art::ProducesCollector& collector, + std::string instanceName = "", + bool doWireAssns = true, + bool doRawDigitAssns = true); + + /** + * @brief Declares the hit products we are going to fill. + * @param collector the module this manager is bound to + * @param instanceName name of the instance for all data products + * @param doWireAssns whether to enable associations to wires + * @param doRawDigitAssns whether to enable associations to raw digits + * + * This declaration must be made in the constructor of producer. + * It is equivalent to manually declare the relevant among these products: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * produces>(prod_instance); + * produces>(prod_instance); + * produces>(prod_instance); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * in the producer constructor. + * All the data products (hit collection and associations) will have the + * specified product instance name. + */ + void declareProducts(art::ProducesCollector& collector, + std::string instanceName = "", + bool doWireAssns = true, + bool doRawDigitAssns = true); + + /// Returns a new writer already configured. + Writer_t collectionWriter(art::Event& event) const; + + /// Returns the configured product instance name. + std::string instanceName() const { return prodInstance; } + + /// Returns whether the class is fully configured. + bool ready() const noexcept { return collector_p != nullptr; } + + protected: + art::ProducesCollector* collector_p = nullptr; ///< Collector this manager is bound to. + + std::string prodInstance; ///< Tame of the instance for data products. + + /// Whether we produce hit-digit associations. + bool hasRawDigitAssns = true; + + /// Whether we produce hit-wire associations. + bool hasWireAssns = true; + + }; // class HitAndAssociationsWriterManager + + /// A manager for `recob::HitCollectionCreator` writer class. + using HitCollectionCreatorManager = HitAndAssociationsWriterManager; + +} // namespace sbn + +//------------------------------------------------------------------------------ +//--- template implementation +//------------------------------------------------------------------------------ +//--- recob::HitAndAssociationsWriterBase +//--- +//------------------------------------------------------------------------------ +//--- recob::HitAndAssociationsWriterManager +//--- +template +sbn::HitAndAssociationsWriterManager::HitAndAssociationsWriterManager( + art::ProducesCollector& collector, + std::string instanceName /* = "" */, + bool doWireAssns /* = true */, + bool doRawDigitAssns /* = true */ +) +{ + declareProducts(collector, instanceName, doWireAssns, doRawDigitAssns); +} // recob::HitAndAssociationsWriterManager::HitAndAssociationsWriterManager() + +//------------------------------------------------------------------------------ +template +void sbn::HitAndAssociationsWriterManager::declareProducts( + art::ProducesCollector& collector, + std::string instanceName /* = "" */, + bool doWireAssns /* = true */, + bool doRawDigitAssns /* = true */ +) +{ + if (collector_p) { + // this means you already called to declaredProducts() + // or used the wrong constructor (which did that for you): + throw art::Exception(art::errors::LogicError) + << "HitAndAssociationsWriter<> has already declared its products."; + } + collector_p = &collector; + prodInstance = instanceName; + hasWireAssns = doWireAssns; + hasRawDigitAssns = doRawDigitAssns; + HitAndAssociationsWriterBase::declare_products( + collector, prodInstance, hasWireAssns, hasRawDigitAssns); +} // recob::HitAndAssociationsWriterManager::declareProducts() + +//------------------------------------------------------------------------------ +template +typename sbn::HitAndAssociationsWriterManager::Writer_t +sbn::HitAndAssociationsWriterManager::collectionWriter(art::Event& event) const +{ + if (!collector_p) { + // this means you forgot to code a call to declaredProducts() + // or used the wrong constructor: + throw art::Exception(art::errors::LogicError) + << "HitAndAssociationsWriter<>::collectionWriter() called" + " before products are declared."; + } + return {event, prodInstance, hasWireAssns, hasRawDigitAssns}; +} // recob::HitAndAssociationsWriterManager::collectionWriter() + +//------------------------------------------------------------------------------ + +#endif // LARDATA_ARTDATAHELPERS_HITCREATOR_H diff --git a/sbncode/HitFinder/WireToChannelROI_module.cc b/sbncode/HitFinder/WireToChannelROI_module.cc new file mode 100644 index 000000000..d8f5062ac --- /dev/null +++ b/sbncode/HitFinder/WireToChannelROI_module.cc @@ -0,0 +1,184 @@ +//////////////////////////////////////////////////////////////////////// +// +// WireToChannelROI class - Convert recob::Wire to recob::ChannelROI +// +// usher@slac.stanford.edu +// +//////////////////////////////////////////////////////////////////////// + +// C/C++ standard libraries +#include +#include +#include // std::pair<> +#include // std::unique_ptr<> + +// framework libraries +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "canvas/Utilities/Exception.h" +#include "canvas/Utilities/InputTag.h" + +// LArSoft libraries +#include "larcoreobj/SimpleTypesAndConstants/RawTypes.h" // raw::ChannelID_t +#include "larcore/Geometry/WireReadout.h" +#include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom() +#include "larcorealg/CoreUtils/zip.h" +#include "lardataobj/RecoBase/Wire.h" + +#include "sbnobj/ICARUS/TPC/ChannelROI.h" +#include "sbnobj/Common/Utilities/ChannelROICreator.h" + +///creation of calibrated signals on wires +namespace caldata { + +class WireToChannelROI : public art::EDProducer +{ +public: +// create calibrated signals on wires. this class runs +// an fft to remove the electronics shaping. + explicit WireToChannelROI(fhicl::ParameterSet const& pset); + void produce(art::Event& evt); + void beginJob(); + void endJob(); + void reconfigure(fhicl::ParameterSet const& p); +private: + + std::vector fWireModuleLabelVec; ///< vector of modules that made digits + std::vector fOutInstanceLabelVec; ///< The output instance labels to apply + short int fMaxADCScaleFactor; ///< Maximum scale factor to apply + bool fDiagnosticOutput; ///< secret diagnostics flag + size_t fEventCount; ///< count of event processed + + const geo::WireReadoutGeom* fChannelMapAlg = &art::ServiceHandle()->Get(); + +}; // class WireToChannelROI + +DEFINE_ART_MODULE(WireToChannelROI) + +//------------------------------------------------- +WireToChannelROI::WireToChannelROI(fhicl::ParameterSet const& pset) : EDProducer{pset} +{ + this->reconfigure(pset); + + for(const auto& wireLabel : fOutInstanceLabelVec) + { + produces>(wireLabel); + } +} + +////////////////////////////////////////////////////// +void WireToChannelROI::reconfigure(fhicl::ParameterSet const& pset) +{ + // Recover the parameters + fWireModuleLabelVec = pset.get>("WireModuleLabelVec", std::vector()={"decon1droi"}); + fOutInstanceLabelVec = pset.get> ("OutInstanceLabelVec", {"PHYSCRATEDATA"}); + fMaxADCScaleFactor = pset.get("MaxADCScaleFactor", 512); + fDiagnosticOutput = pset.get< bool >("DiagnosticOutput", false); + + if (fWireModuleLabelVec.size() != fOutInstanceLabelVec.size()) + { + throw art::Exception(art::errors::Configuration) << " Configured " << fOutInstanceLabelVec.size() + << " instance names (`OutInstanceLabelVec`) for " << fWireModuleLabelVec.size() + << " input products (`WireModuleLabelVec`)\n"; + } + + return; +} + +//------------------------------------------------- +void WireToChannelROI::beginJob() +{ + fEventCount = 0; +} // beginJob + +////////////////////////////////////////////////////// +void WireToChannelROI::endJob() +{ +} + +////////////////////////////////////////////////////// +void WireToChannelROI::produce(art::Event& evt) +{ + // We need to loop through the list of Wire data we have been given + // This construct from Gianluca Petrillo who invented it and should be given all credit for it! + for(auto const& [wireLabel, instanceName] : util::zip(fWireModuleLabelVec, fOutInstanceLabelVec)) + { + // make a collection of Wires + std::unique_ptr> channelROICol = std::make_unique>(); + + mf::LogInfo("WireToChannelROI") << "WireToChannelROI, looking for Wire data at " << wireLabel.encode(); + + // Read in the collection of full length deconvolved waveforms + const std::vector& wireVec = evt.getProduct>(wireLabel); + + mf::LogInfo("WireToChannelROI") << "--> Recovered Wire data, size: " << wireVec.size(); + + if (!wireVec.empty()) + { + // Reserve the room for the output + channelROICol->reserve(wireVec.size()); + + // Loop through the input ChannelROI collection + for(const auto& wire : wireVec) + { + short int ADCScaleFactor(recob::ChannelROI::defADCScaleFactor); + + // Recover the channel and the view + raw::ChannelID_t channel = wire.Channel(); + + // Create an ROI vector for output + recob::ChannelROI::RegionsOfInterest_t ROIVec; + + // Return the entire waveform (zero padded) so we can find maximum range + std::vector fullWaveform = wire.Signal(); + + std::pair::const_iterator,std::vector::const_iterator> minMaxPair = std::minmax_element(fullWaveform.begin(),fullWaveform.end()); + + float maxValue = std::max(-*minMaxPair.first,*minMaxPair.second); + + ADCScaleFactor = std::max(short(std::round(std::min(float(fMaxADCScaleFactor),static_cast(std::numeric_limits::max())/maxValue))),ADCScaleFactor); + + // Loop through the ROIs for this channel + const recob::Wire::RegionsOfInterest_t& wireROIs = wire.SignalROI(); + + for(const auto& range : wireROIs.get_ranges()) + { + size_t startTick = range.begin_index(); + + std::vector dataVec(range.data().size()); + + for(size_t binIdx = 0; binIdx < range.data().size(); binIdx++) + { + short int scaledIntADCVal = + std::min(std::max(std::round(range.data()[binIdx] * ADCScaleFactor),static_cast(std::numeric_limits::min())), + static_cast(std::numeric_limits::max())); + + dataVec[binIdx] = scaledIntADCVal; + } + + ROIVec.add_range(startTick, std::move(dataVec)); + } + + channelROICol->push_back(recob::ChannelROICreator(std::move(ROIVec),channel,ADCScaleFactor).move()); + } + + mf::LogInfo("WireToChannelROI") << "--> Outputting ChannelROI data, size: " << channelROICol->size() << " with instance name: " << instanceName; + + // Time to stroe everything + if(channelROICol->empty()) mf::LogWarning("WireToChannelROI") << "No wires made for this event."; + } + + evt.put(std::move(channelROICol), instanceName); + } + + fEventCount++; + + return; +} // produce + +} // end namespace caldata diff --git a/sbncode/HitFinder/hitfindermodules_sbn.fcl b/sbncode/HitFinder/hitfindermodules_sbn.fcl new file mode 100644 index 000000000..f6def3d0a --- /dev/null +++ b/sbncode/HitFinder/hitfindermodules_sbn.fcl @@ -0,0 +1,54 @@ +// from larreco/RecoAlg: +#include "hitalgorithms.fcl" +#include "hitfindermodules.fcl" + +BEGIN_PROLOG + +# This defines a version of the Gauss Hit Finder that uses as input ChannelROIs instead of Wire +# In principle all default parameters remain the same. + +gauss_hitfinder: +{ + module_type: "GaussHitFinderSBN" + CalDataModuleLabel: "caldata" + MaxMultiHit: 5 # maximum hits for multi gaussian fit attempt + AreaMethod: 0 # 0 = area by integral, 1 = area by gaussian area formula + AreaNorms: [ 1.0, 1.0, 1.0 ] # normalizations that put signal area in + # same scale as peak height. + TryNplus1Fits: false # Don't try to refit with extra peak if bad chisq + LongMaxHits: [ 25, 25, 25] # max number hits in long pulse trains + LongPulseWidth: [ 5, 5, 5] # max widths for hits in long pulse trains + Chi2NDF: 500. # maximum Chisquared / NDF allowed to store fit + # will use "long" pulse method to return hit + AllHitsInstanceName: "" # If non-null then this will be the instance name of all hits output to event + # in this case there will be two hit collections, one filtered and one containing all hits + + # Candididate peak finding done by tool, one tool instantiated per plane (but could be other divisions too) + HitFinderToolVec: + { + CandidateHitsPlane0: @local::candhitfinder_standard # plane 0 + CandidateHitsPlane1: @local::candhitfinder_standard # plane 1 + CandidateHitsPlane2: @local::candhitfinder_standard # plane 2 + } + + # Declare the peak fitting tool + PeakFitter: @local::peakfitter_mrqdt + + # The below are for the hit filtering section of the gaushit finder + FilterHits: false # true = do not keep undesired hits according to settings of HitFilterAlg object + HitFilterAlg: + { + AlgName: "HitFilterAlg" + MinPulseHeight: [2.0, 2.0, 2.0] #minimum hit peak amplitude per plane + MinPulseSigma: [1.0, 1.0, 1.0] #minimum hit rms per plane + } + # In addition to the filter alg we can also filter hits on the same pulse train + PulseHeightCuts: [3.0, 3.0, 3.0 ] # Minimum pulse height + PulseWidthCuts: [2.0, 1.5, 1.0 ] # Minimum pulse width + PulseRatioCuts: [0.35, 0.40, 0.20] # Ratio of pulse height to width +} + +# Define sbn version of gaushit finder +gausshit_sbn: @local::gauss_hitfinder + +END_PROLOG diff --git a/sbncode/HitFinder/wirechannelroiconverters_sbn.fcl b/sbncode/HitFinder/wirechannelroiconverters_sbn.fcl new file mode 100644 index 000000000..a79623b16 --- /dev/null +++ b/sbncode/HitFinder/wirechannelroiconverters_sbn.fcl @@ -0,0 +1,30 @@ +// from larreco/RecoAlg: +#include "hitalgorithms.fcl" +#include "hitfindermodules.fcl" + +BEGIN_PROLOG + +# This defines a version of the Gauss Hit Finder that uses as input ChannelROIs instead of Wire +# In principle all default parameters remain the same. + +channelroitowire: +{ + module_type: "ChannelROIToWire" + WireModuleLabelVec: [" "] + OutInstanceLabelVec: [" "] + DiagnosticOutput: false +} + +wiretochannelroi: +{ + module_type: "WireToChannelROI" + WireModuleLabelVec: [" "] + OutInstanceLabelVec: [" "] + DiagnosticOutput: false +} + +# Define sbn versions +channelroitowire_sbn: @local::channelroitowire +wiretochannelroi_sbn: @local::wiretochannelroi + +END_PROLOG diff --git a/sbncode/LArG4/CMakeLists.txt b/sbncode/LArG4/CMakeLists.txt index dd353af96..4a00b17ef 100644 --- a/sbncode/LArG4/CMakeLists.txt +++ b/sbncode/LArG4/CMakeLists.txt @@ -1,15 +1,5 @@ add_subdirectory(PhysicsLists) -cet_build_plugin(G4InfoReducer art::module - LIBRARIES - larsim::Simulation - larcore::Geometry_Geometry_service - lardata::DetectorInfoServices_DetectorClocksServiceStandard_service - art::Persistency_Common - art::Persistency_Provenance - art::Utilities canvas::canvas - BASENAME_ONLY) - set( MODULE_LIBRARIES art::Utilities @@ -35,7 +25,7 @@ set( sbncode_LArG4_PhysicsLists ) -cet_build_plugin(PhysListLoader art::module SOURCE PhysListLoader_module.cc LIBRARIES ${MODULE_LIBRARIES}) +cet_build_plugin(PhysListLoader art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) cet_make_library(SOURCE MergeSimSourcesLiteUtility.cxx LIBRARIES @@ -45,7 +35,7 @@ cet_make_library(SOURCE MergeSimSourcesLiteUtility.cxx ) cet_build_plugin(MergeSimSourcesSBN art::EDProducer - LIBRARIES + LIBRARIES PRIVATE sbncode::LArG4 larsim::MergeSimSources lardataobj::MCBase @@ -61,14 +51,5 @@ cet_build_plugin(MergeSimSourcesSBN art::EDProducer BASENAME_ONLY ) -cet_build_plugin(SimpleMerge art::EDProducer - LIBRARIES - lardataobj::MCBase - larcorealg::headers - larcorealg::CoreUtils - messagefacility::MF_MessageLogger -) - install_headers() -install_fhicl() install_source() diff --git a/sbncode/LArG4/MergeSimSourcesSBN_module.cc b/sbncode/LArG4/MergeSimSourcesSBN_module.cc index da84303a7..19e1cbe1c 100644 --- a/sbncode/LArG4/MergeSimSourcesSBN_module.cc +++ b/sbncode/LArG4/MergeSimSourcesSBN_module.cc @@ -97,6 +97,12 @@ class sbn::MergeSimSourcesSBN : public art::EDProducer { true // default }; + fhicl::Atom SkipSimChannelTrackIDs{ + fhicl::Name{"SkipSimChannelTrackIDs"}, + fhicl::Comment{"Skip G4 track IDs in SimChannels"}, + false // default + }; + fhicl::Atom FillAuxDetSimChannels{ fhicl::Name{"FillAuxDetSimChannels"}, fhicl::Comment{"whether to merge AuxDetSimChannels"}, @@ -156,6 +162,7 @@ class sbn::MergeSimSourcesSBN : public art::EDProducer { bool const fFillMCParticlesAssociated; bool const fFillSimPhotons; bool const fFillSimChannels; + bool const fSkipSimChannelTrackIDs; bool const fFillAuxDetSimChannels; bool const fFillSimEnergyDeposits; std::vector const fEnergyDepositionInstances; @@ -198,6 +205,7 @@ sbn::MergeSimSourcesSBN::MergeSimSourcesSBN(Parameters const& params) , fFillMCParticlesAssociated(params().FillMCParticlesAssociated()) , fFillSimPhotons(params().FillSimPhotons()) , fFillSimChannels(params().FillSimChannels()) + , fSkipSimChannelTrackIDs(params().SkipSimChannelTrackIDs()) , fFillAuxDetSimChannels(params().FillAuxDetSimChannels()) , fFillSimEnergyDeposits( getOptionalValue(params().FillSimEnergyDeposits) @@ -349,7 +357,7 @@ void sbn::MergeSimSourcesSBN::produce(art::Event& e) if (fFillSimChannels) { auto const& input_scCol = e.getProduct>(input_label); - MergeUtility.MergeSimChannels(*scCol, input_scCol, i_source); + MergeUtility.MergeSimChannels(*scCol, input_scCol, i_source, fSkipSimChannelTrackIDs); } if (fFillAuxDetSimChannels) { @@ -451,6 +459,7 @@ void sbn::MergeSimSourcesSBN::dumpConfiguration() const if (fFillMCParticlesAssociated) log << "\n - filling MCParticlesAssociated"; if (fFillSimChannels) log << "\n - filling SimChannels"; + if (fSkipSimChannelTrackIDs) log << "\n - skipping track IDs in filling SimChannels"; if (fFillAuxDetSimChannels) log << "\n - filling AuxDetSimChannels"; diff --git a/sbncode/LArG4/g4inforeducer.fcl b/sbncode/LArG4/g4inforeducer.fcl deleted file mode 100644 index ced065f67..000000000 --- a/sbncode/LArG4/g4inforeducer.fcl +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN_PROLOG - -sbn_largeant_info_reducer: { - module_type: "G4InfoReducer" - SimEnergyDepositLabel: "largeant:TPCActive" - VoxelSizeX: 0.3 - VoxelSizeY: 0.3 - VoxelSizeZ: 0.3 - useOrigTrackID: true -} - -END_PROLOG diff --git a/sbncode/LArG4/simplemerge.fcl b/sbncode/LArG4/simplemerge.fcl deleted file mode 100644 index 01ad905be..000000000 --- a/sbncode/LArG4/simplemerge.fcl +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN_PROLOG - -simplemerge : { - module_type: "SimpleMerge" - InputSourcesLabels: ["largeant", "largeantdropped"] - ResetMotherIDs: [ 10000000, 20000000 ] -} - -END_PROLOG \ No newline at end of file diff --git a/sbncode/LArRecoProducer/CMakeLists.txt b/sbncode/LArRecoProducer/CMakeLists.txt index 3f3fe7b4b..e0463b151 100644 --- a/sbncode/LArRecoProducer/CMakeLists.txt +++ b/sbncode/LArRecoProducer/CMakeLists.txt @@ -28,18 +28,18 @@ set( MODULE_LIBRARIES lardataobj::RecoBase ) -cet_build_plugin(CRTHitConverter art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(MCSFitAllPID art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(PMTFlashTriggerFilter art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(PMTFlashTriggerMaker art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(RangePAllPID art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(ShowerCosmicDistance art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(ShowerSelectionVars art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(TrackScatterClosestApproach art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(TrackStoppingChi2Fitter art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(TransferPFParticleFlashMatch art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(TransferPFParticleT0 art::module LIBRARIES ${MODULE_LIBRARIES}) -cet_build_plugin(TransferTrackT0 art::module LIBRARIES ${MODULE_LIBRARIES}) +cet_build_plugin(CRTHitConverter art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(MCSFitAllPID art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(PMTFlashTriggerFilter art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(PMTFlashTriggerMaker art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(RangePAllPID art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(ShowerCosmicDistance art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(ShowerSelectionVars art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(TrackScatterClosestApproach art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(TrackStoppingChi2Fitter art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(TransferPFParticleFlashMatch art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(TransferPFParticleT0 art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) +cet_build_plugin(TransferTrackT0 art::module LIBRARIES PRIVATE ${MODULE_LIBRARIES}) install_headers() install_fhicl() diff --git a/sbncode/Metadata/CMakeLists.txt b/sbncode/Metadata/CMakeLists.txt index 6cf9127e5..0d5111601 100644 --- a/sbncode/Metadata/CMakeLists.txt +++ b/sbncode/Metadata/CMakeLists.txt @@ -1,5 +1,25 @@ cet_enable_asserts() +GenerateRepoVersionSource(${CMAKE_PROJECT_NAME}) + +message(DEBUG "Generated source file '${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.h'") + +art_make_library( + SOURCE + RepositoryVersion_sbncode.cxx # generated by GenerateRepoVersionSource() above + JobEnvironmentInfoExtractor.cxx + RepositoryVersionReportUtils.cpp + LIBRARIES + sbnobj::Common_Metadata + larcorealg::CoreUtils + art::Persistency_Provenance + art::Utilities + canvas::canvas + messagefacility::MF_MessageLogger + fhiclcpp::fhiclcpp + ) + + set( sbn_meta_lib_list art::Framework_Core art::Framework_Principal art::Framework_Services_Registry @@ -21,6 +41,30 @@ cet_build_plugin( MetadataSBN art::service art::Framework_IO_detail ) +cet_build_plugin(SaveJobEnvironment art::ResultsProducer + LIBRARIES + sbncode::Metadata + sbnobj::Common_Metadata + art_root_io::TFileService_service + art_root_io::tfile_support + art::Framework_Services_Registry + ) + +cet_build_plugin(DumpJobEnvironment art::Output + LIBRARIES + sbnobj::Common_Metadata + ) + +cet_build_plugin(sbncodeRepositoryVersion art::tool + LIBRARIES + sbncode::Metadata + sbnobj::Metadata + sbnobj::Common_Metadata + ) + + +install_headers(LIST "${CMAKE_CURRENT_BINARY_DIR}/${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.h") +install_source(LIST "${CMAKE_CURRENT_BINARY_DIR}/${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.cxx") install_headers() install_fhicl() install_source() diff --git a/sbncode/Metadata/DumpJobEnvironment_module.cc b/sbncode/Metadata/DumpJobEnvironment_module.cc new file mode 100644 index 000000000..93e4b8eb8 --- /dev/null +++ b/sbncode/Metadata/DumpJobEnvironment_module.cc @@ -0,0 +1,176 @@ +/** + * @file sbncode/Metadata/DumpJobEnvironment_module.cc + * @brief Producer module writing job environment information into output. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 16, 2025 + */ + +// local libraries +#include "sbnobj/Common/Metadata/JobEnvironmentInfo.h" + +// framework libraries +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Core/OutputModule.h" +#include "art/Framework/Principal/ResultsPrincipal.h" +#include "art/Framework/Principal/Results.h" +#include "art/Framework/Principal/Provenance.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Persistency/Provenance/ModuleContext.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "fhiclcpp/types/ConfigurationTable.h" // fhicl::WrappedTable +#include "fhiclcpp/types/TableFragment.h" +#include "fhiclcpp/types/Atom.h" + +// C++ standard libraries +#include +#include + + +// ----------------------------------------------------------------------------- +namespace sbn { class DumpJobEnvironment; } +/** + * @brief Output module dumping input versions to screen. + * + * The output module can be added to any of the end paths of an _art_ job to + * get a complete dump of the SBN job environment metadata stored into the input + * file. + * + * For example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * process_name: JobMeta + * + * services.message.destinations.MetadataLog: { + * type: file + * filename: "JobEnvironment.log" + * append: false + * threshold: INFO + * categories: { + * DumpJobEnvironment: { limit: -1 } + * default: { limit: 0 } + * } + * } + * + * outputs.metadataDumper: { module_type: "DumpJobEnvironment" } + * + * physics.streams: [ metadataDumper ] + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * will dump the metadata into a text file named `JobEnvironment.log`. + * + */ +class sbn::DumpJobEnvironment: public art::OutputModule { + public: + + /// Module configuration. + struct Config { + + using Name = fhicl::Name; + using Comment = fhicl::Comment; + + fhicl::TableFragment OutputModuleConfig; + + fhicl::Atom LogCategory { + Name{ "LogCategory" }, + Comment{ "name of the messagefacility output category to be used" }, + "DumpJobEnvironment" + }; + + }; // Config + + using Parameters + = fhicl::WrappedTable; + + explicit DumpJobEnvironment(Parameters const& params); + + private: + + // --- BEGIN --- Configuration ----------------------------------------------- + + std::string const fLogCategory; ///< Messagefacility category for the output. + + // --- END ----- Configuration ----------------------------------------------- + + + /// Dumps the information from an handle into the output stream. + template + void dumpInformation( + Stream& out, + art::Handle> const& infoHandle + ) const; + + + + void write(art::EventPrincipal&) override {} + void writeRun(art::RunPrincipal&) override {} + void writeSubRun(art::SubRunPrincipal&) override {} + + /// Reads and prints all the metadata data products. + void readResults(art::ResultsPrincipal const& results) override; + +}; // sbn::DumpJobEnvironment + + +// ----------------------------------------------------------------------------- +// --- Implementation +// ----------------------------------------------------------------------------- +sbn::DumpJobEnvironment::DumpJobEnvironment(Parameters const& params) + : OutputModule{ params().OutputModuleConfig } + , fLogCategory{ params().LogCategory() } +{} + + +// ----------------------------------------------------------------------------- +void sbn::DumpJobEnvironment::readResults + (art::ResultsPrincipal const& principal) +{ + if (!principal.size()) return; + + art::ModuleContext const moduleContext{ moduleDescription() }; + art::Results const& results = principal.makeResults(moduleContext); + + std::vector>> infoHandles + = results.getMany>(); + + mf::LogInfo out{ fLogCategory }; + out << "Found " << infoHandles.size() << " job information entries in input."; + + for (art::Handle> const& infoHandle + : infoHandles + ) { + out << '\n' << std::string(80, '*') << '\n'; + dumpInformation(out, infoHandle); + } + +} // sbn::DumpJobEnvironment::readResults() + + +// ----------------------------------------------------------------------------- +template +void sbn::DumpJobEnvironment::dumpInformation( + Stream& out, + art::Handle> const& infoHandle +) const { + + art::Provenance const* provenance = infoHandle.provenance(); + if (provenance) { + out << "Information from '" << provenance->inputTag().encode() << "'"; + } + else { + out << "Information with unknown provenance"; + } + + if (infoHandle.isValid()) { + out << " from " << infoHandle->size() << " sources\n"; + for (sbn::JobEnvironmentInfo const& info: *infoHandle) { + out << std::string(80, '=') << '\n' << info; + } + } + else out << "\n[information not available]\n"; + +} // sbn::DumpJobEnvironment::dumpInformation() + + +// ----------------------------------------------------------------------------- +DEFINE_ART_MODULE(sbn::DumpJobEnvironment) + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/Metadata/FileCatalogMetadataSBN.cc b/sbncode/Metadata/FileCatalogMetadataSBN.cc new file mode 100644 index 000000000..7c4d8f613 --- /dev/null +++ b/sbncode/Metadata/FileCatalogMetadataSBN.cc @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////// +// Name: FileCatalogMetadataSBN.cc. +// +// Purpose: Implementation for FileCatalogMetadataSBN. +// +// Created: 28-Oct-2014, H. Greenlee +// +//////////////////////////////////////////////////////////////////////// + +#include "sbncode/Metadata/FileCatalogMetadataSBN.h" + +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "art/Framework/Services/System/FileCatalogMetadata.h" +#include "art/Framework/Principal/SubRun.h" +#include "art/Framework/Principal/Handle.h" +#include "cetlib_except/exception.h" +#include "larcoreobj/SummaryData/POTSummary.h" + +//-------------------------------------------------------------------- +// Constructor. + +util::FileCatalogMetadataSBN:: +FileCatalogMetadataSBN(fhicl::ParameterSet const& pset, art::ActivityRegistry& reg) : + fTotPOT(0.) +{ + // Insist on configuring Experiment from the fcl file (ideally) or the + // environment. + const char* expt = getenv("EXPERIMENT"); + if(expt) fExperiment = pset.get("Experiment", expt); else fExperiment = pset.get("Experiment"); + std::transform(fExperiment.begin(), fExperiment.end(), fExperiment.begin(), [](unsigned char c){return std::tolower(c);}); + + // Get parameters. + + fFCLName = pset.get("FCLName"); + fProjectName = pset.get("ProjectName"); + fProjectStage = pset.get("ProjectStage"); + fProjectVersion = pset.get("ProjectVersion"); + fProjectSoftware = pset.get("ProjectSoftware",""); + fProductionName = pset.get("ProductionName",""); //Leave as default value if not running a production + fProductionType = pset.get("ProductionType",""); //Leave as default value if not running a production + fMerge = pset.get("Merge", -1); + fParameters = pset.get>>("Parameters", std::vector>()); + fPOTModuleLabel = pset.get("POTModuleLabel", "generator"); + + // Register for callbacks. + + reg.sPostBeginJob.watch(this, &FileCatalogMetadataSBN::postBeginJob); + reg.sPostEndSubRun.watch(this, &FileCatalogMetadataSBN::postEndSubRun); + reg.sPreCloseOutputFile.watch(this, &FileCatalogMetadataSBN::preCloseOutputFile); +} + +//-------------------------------------------------------------------- +// PostBeginJob callback. +// Insert per-job metadata via FileCatalogMetadata service. +void util::FileCatalogMetadataSBN::postBeginJob() +{ + // Get art metadata service. + + art::ServiceHandle mds; + + // Add metadata. + + if (!fFCLName.empty()) mds->addMetadata("fcl.name", fFCLName); + if (!fProjectName.empty()) mds->addMetadata(fExperiment + "_project.name", fProjectName); + if (!fProjectStage.empty()) mds->addMetadata(fExperiment + "_project.stage", fProjectStage); + if (!fProjectVersion.empty()) mds->addMetadata(fExperiment + "_project.version", fProjectVersion); + if (!fProjectSoftware.empty()) mds->addMetadata(fExperiment + "_project.software", fProjectSoftware); + if (!fProductionName.empty()) mds->addMetadata("production.name", fProductionName); + if (!fProductionType.empty()) mds->addMetadata("production.type", fProductionType); + std::ostringstream ostr; + if(fMerge >= 0) { + if(fMerge > 0) + mds->addMetadata("merge.merge", "1"); + else + mds->addMetadata("merge.merge", "0"); + mds->addMetadata("merge.merged", "0"); + } + for(auto const& param : fParameters) + mds->addMetadata(param.first, param.second); +} + +//-------------------------------------------------------------------- +// PostEndSubrun callback. +void util::FileCatalogMetadataSBN::postEndSubRun(art::SubRun const& sr) +{ + + art::ServiceHandle mds; + + art::Handle< sumdata::POTSummary > potListHandle; + if(sr.getByLabel(fPOTModuleLabel,potListHandle)){ + std::lock_guard lock(fMutex); + fTotPOT+=potListHandle->totpot; + } +} + +// PreCloseOutputFile callback. +void util::FileCatalogMetadataSBN::preCloseOutputFile(const std::string& label) +{ + std::lock_guard lock(fMutex); + + art::ServiceHandle mds; + + if(fTotPOT > 0.) { + std::ostringstream streamObj; + streamObj << fTotPOT; + std::string strPOT = streamObj.str(); + mds->addMetadata("mc.pot", strPOT); + } + fTotPOT = 0.; +} diff --git a/sbncode/Metadata/FileCatalogMetadataSBN_service.cc b/sbncode/Metadata/FileCatalogMetadataSBN_service.cc index d16ce8b42..73c5ec7e4 100644 --- a/sbncode/Metadata/FileCatalogMetadataSBN_service.cc +++ b/sbncode/Metadata/FileCatalogMetadataSBN_service.cc @@ -1,113 +1,4 @@ -//////////////////////////////////////////////////////////////////////// -// Name: FileCatalogMetadataSBN_service.cc. -// -// Purpose: Implementation for FileCatalogMetadataSBN. -// -// Created: 28-Oct-2014, H. Greenlee -// -//////////////////////////////////////////////////////////////////////// - -#include "sbncode/Metadata/FileCatalogMetadataSBN.h" - -#include "art/Framework/Services/Registry/ServiceHandle.h" #include "art/Framework/Services/Registry/ServiceDefinitionMacros.h" -#include "art/Framework/Services/System/FileCatalogMetadata.h" -#include "art/Framework/Principal/SubRun.h" -#include "art/Framework/Principal/Handle.h" -#include "cetlib_except/exception.h" -#include "larcoreobj/SummaryData/POTSummary.h" - -//-------------------------------------------------------------------- -// Constructor. - -util::FileCatalogMetadataSBN:: -FileCatalogMetadataSBN(fhicl::ParameterSet const& pset, art::ActivityRegistry& reg) : - fTotPOT(0.) -{ - // Insist on configuring Experiment from the fcl file (ideally) or the - // environment. - const char* expt = getenv("EXPERIMENT"); - if(expt) fExperiment = pset.get("Experiment", expt); else fExperiment = pset.get("Experiment"); - std::transform(fExperiment.begin(), fExperiment.end(), fExperiment.begin(), [](unsigned char c){return std::tolower(c);}); - - // Get parameters. - - fFCLName = pset.get("FCLName"); - fProjectName = pset.get("ProjectName"); - fProjectStage = pset.get("ProjectStage"); - fProjectVersion = pset.get("ProjectVersion"); - fProjectSoftware = pset.get("ProjectSoftware",""); - fProductionName = pset.get("ProductionName",""); //Leave as default value if not running a production - fProductionType = pset.get("ProductionType",""); //Leave as default value if not running a production - fMerge = pset.get("Merge", -1); - fParameters = pset.get>>("Parameters", std::vector>()); - fPOTModuleLabel = pset.get("POTModuleLabel", "generator"); - - // Register for callbacks. - - reg.sPostBeginJob.watch(this, &FileCatalogMetadataSBN::postBeginJob); - reg.sPostEndSubRun.watch(this, &FileCatalogMetadataSBN::postEndSubRun); - reg.sPreCloseOutputFile.watch(this, &FileCatalogMetadataSBN::preCloseOutputFile); -} - -//-------------------------------------------------------------------- -// PostBeginJob callback. -// Insert per-job metadata via FileCatalogMetadata service. -void util::FileCatalogMetadataSBN::postBeginJob() -{ - // Get art metadata service. - - art::ServiceHandle mds; - - // Add metadata. - - mds->addMetadata("fcl.name", fFCLName); - mds->addMetadata(fExperiment + "_project.name", fProjectName); - mds->addMetadata(fExperiment + "_project.stage", fProjectStage); - mds->addMetadata(fExperiment + "_project.version", fProjectVersion); - mds->addMetadata(fExperiment + "_project.software", fProjectSoftware); - mds->addMetadata("production.name", fProductionName); - mds->addMetadata("production.type", fProductionType); - std::ostringstream ostr; - if(fMerge >= 0) { - if(fMerge > 0) - mds->addMetadata("merge.merge", "1"); - else - mds->addMetadata("merge.merge", "0"); - mds->addMetadata("merge.merged", "0"); - } - for(auto const& param : fParameters) - mds->addMetadata(param.first, param.second); -} - -//-------------------------------------------------------------------- -// PostEndSubrun callback. -void util::FileCatalogMetadataSBN::postEndSubRun(art::SubRun const& sr) -{ - - art::ServiceHandle mds; - - art::Handle< sumdata::POTSummary > potListHandle; - if(sr.getByLabel(fPOTModuleLabel,potListHandle)){ - std::lock_guard lock(fMutex); - fTotPOT+=potListHandle->totpot; - } -} - -// PreCloseOutputFile callback. -void util::FileCatalogMetadataSBN::preCloseOutputFile(const std::string& label) -{ - std::lock_guard lock(fMutex); - - art::ServiceHandle mds; - - if(fTotPOT > 0.) { - std::ostringstream streamObj; - streamObj << fTotPOT; - std::string strPOT = streamObj.str(); - mds->addMetadata("mc.pot", strPOT); - } - fTotPOT = 0.; -} +#include "sbncode/Metadata/FileCatalogMetadataSBN.h" DEFINE_ART_SERVICE(util::FileCatalogMetadataSBN) diff --git a/sbncode/Metadata/GITrepoVersion.cxx.in b/sbncode/Metadata/GITrepoVersion.cxx.in new file mode 100644 index 000000000..0d2a35157 --- /dev/null +++ b/sbncode/Metadata/GITrepoVersion.cxx.in @@ -0,0 +1,16 @@ +/** + * @file ${gitRepoVersionSourceStem}.cxx + * @brief GIT version for repository `${gitRepoName}`. + * @author automatically generated; + * template: Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * @see ${gitRepoVersionSourceStem}.h + */ + +#include "${gitRepoVersionSourceStem}.h" + +// ----------------------------------------------------------------------------- +const char RepositoryVersion_${gitRepoName}[] = "${gitRepoVersion}"; + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/Metadata/GITrepoVersion.h.in b/sbncode/Metadata/GITrepoVersion.h.in new file mode 100644 index 000000000..a3210703d --- /dev/null +++ b/sbncode/Metadata/GITrepoVersion.h.in @@ -0,0 +1,18 @@ +/** + * @file ${gitRepoVersionSourceStem}.h + * @brief GIT version for repository `${gitRepoName}`. + * @author automatically generated; + * template: Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * @see ${gitRepoVersionSourceStem}.cxx + */ + +#ifndef ${gitRepoName}_${gitRepoVersionSourceStem}_H +#define ${gitRepoName}_${gitRepoVersionSourceStem}_H + + +/// Repository version for ${gitRepoName}. +extern const char RepositoryVersion_${gitRepoName}[]; + + +#endif // ${gitRepoName}_${gitRepoVersionSourceStem}_H diff --git a/sbncode/Metadata/JobEnvironmentInfoExtractor.cxx b/sbncode/Metadata/JobEnvironmentInfoExtractor.cxx new file mode 100644 index 000000000..4a7ec971f --- /dev/null +++ b/sbncode/Metadata/JobEnvironmentInfoExtractor.cxx @@ -0,0 +1,149 @@ +/** + * @file sbncode/Metadata/JobEnvironmentInfoExtractor.cxx + * @brief Algorithm extracting information from the job execution environment. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 16, 2025 + * @see sbncode/Metadata/JobEnvironmentInfoExtractor.h + */ + +// library header +#include "sbncode/Metadata/JobEnvironmentInfoExtractor.h" + +// SBN libraries +#include "sbncode/Metadata/RepositoryVersionReportUtils.h" +#include "sbncode/Metadata/RepositoryVersionReporter.h" + +// LArSoft libraries +#include "larcorealg/CoreUtils/enumerate.h" + +// framework libraries +#include "art/Persistency/Provenance/ModuleDescription.h" +#include "art/Utilities/make_tool.h" +#include "canvas/Utilities/Exception.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "fhiclcpp/ParameterSet.h" + +// C/C++ standard library +#include +#include +#include // std::move() + +// system libraries +#include // strchr() +#include // environ + + +// ----------------------------------------------------------------------------- +std::vector const +sbn::JobEnvironmentInfoExtractor::Config::DefaultDropVars = { + R"|(.*%%$)|" // BASH function pattern + , R"|(.*\(\)$)|" // another BASH function pattern +}; + + + +// ----------------------------------------------------------------------------- +sbn::JobEnvironmentInfoExtractor::JobEnvironmentInfoExtractor + (Config const& config /* = Config{} */) + : fDropVars{ prepareRegEx(config.dropVars) } + , fRepositoryReports{ config.repositories } + , fLogCategory{ config.logCategory } +{ + +} + + +// ----------------------------------------------------------------------------- +sbn::JobEnvironmentInfo sbn::JobEnvironmentInfoExtractor::extract + (art::ModuleDescription const& moduleInfo) +{ + + sbn::JobEnvironmentInfo info; + + info.processName = moduleInfo.processName(); + info.artVersion = moduleInfo.releaseVersion(); + + info.sources = extractSourceVersions(); + + info.variables = extractEnvironmentVariables(); + + return info; + +} // sbn::JobEnvironmentInfoExtractor::extract() + + +// ----------------------------------------------------------------------------- +auto sbn::JobEnvironmentInfoExtractor::extractSourceVersions() const + -> sbn::OrderedPairList +{ + + // take advantage of the basic + sbn::OrderedPairList sourceVersions; + addVersionFromRepositories(sourceVersions, fRepositoryReports); + sourceVersions.finish(); + + return sourceVersions; + +} // sbn::JobEnvironmentInfoExtractor::extractSourceVersions() + + +// ----------------------------------------------------------------------------- +auto sbn::JobEnvironmentInfoExtractor::extractEnvironmentVariables() const + -> sbn::OrderedPairList +{ + + sbn::OrderedPairList vars; + + char** itemPtr = environ; + while (const char* item = *(itemPtr++)) { + + const char* sep = strchr(item, '='); + + std::string name, value; + if (sep) { + name = std::string{ item, sep++ }; + value = std::string{ sep }; + } + else { + mf::LogDebug{ fLogCategory } + << "Environment element '" << item << "' is not in name=value form."; + name = item; + } + + // apply drop filters + if (matchPatterns(name, fDropVars) < fDropVars.size()) + continue; + + vars.items.emplace_back(std::move(name), std::move(value)); + + } // while + + vars.finish(); + + return vars; + +} // sbn::JobEnvironmentInfoExtractor::extractEnvironmentVariables() + + +// ----------------------------------------------------------------------------- +std::size_t sbn::JobEnvironmentInfoExtractor::matchPatterns + (std::string const& value, std::vector const& patterns) +{ + for (auto const& [ iPattern, pattern ]: util::enumerate(patterns)) + if (std::regex_match(value, pattern)) return iPattern; + return patterns.size(); +} + + +// ----------------------------------------------------------------------------- +std::vector sbn::JobEnvironmentInfoExtractor::prepareRegEx + (std::vector const& patterns) +{ + std::vector regex; + regex.reserve(patterns.size()); + for (std::string const& pattern: patterns) regex.emplace_back(pattern); + return regex; +} + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/Metadata/JobEnvironmentInfoExtractor.h b/sbncode/Metadata/JobEnvironmentInfoExtractor.h new file mode 100644 index 000000000..4c480a3a2 --- /dev/null +++ b/sbncode/Metadata/JobEnvironmentInfoExtractor.h @@ -0,0 +1,146 @@ +/** + * @file sbncode/Metadata/JobEnvironmentInfoExtractor.h + * @brief Algorithm extracting information from the job execution environment. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 16, 2025 + * @see sbncode/Metadata/JobEnvironmentInfoExtractor.cxx + */ + +#ifndef SBNCODE_METADATA_JOBENVIRONMENTINFOEXTRACTOR_H +#define SBNCODE_METADATA_JOBENVIRONMENTINFOEXTRACTOR_H + + +// local libraries +#include "sbnobj/Common/Metadata/JobEnvironmentInfo.h" + +// C++ standard libraries +#include +#include +#include + + +// ----------------------------------------------------------------------------- +namespace art { class ModuleDescription; } // forward declarations + +// ----------------------------------------------------------------------------- +namespace sbn { class JobEnvironmentInfoExtractor; } +/** + * @brief Extracts job execution environment information. + * + * In its simplest form, the information is saved into a + * `sbn::JobEnvironmentInfo` object: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * sbn::JobEnvironmentInfoExtractor extractor; + * sbn::JobEnvironmentInfo const info = extractor(moduleDescription()); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The `moduleDescription()` is a _art_ module member function, and as such this + * call is bound to the _art_ framework. However, if the context does not allow + * for a `art::ModuleDescription` object, partial information can still be + * retrieved which does not need that object. + * + * + * Configuration + * -------------- + * + * The configuration object, `Config`, includes the following configurable + * elements: + * * `dropVars`: a list of regular expression (`std::regex`) patterns to be + * matched to the environment variable names; if the name of a variable + * matches any of the the full patterns specified here, its value is not + * included in the metadata. This is useful to exclude known irrelevant + * variables (for example, shell functions). + * * `repositories`: a list of names of repositories to query for their + * version. For each repository ``, an attempt to load a _art_ tool + * named `RepositoryVersion` with an empty configuration parameter + * set is attempted, and on success all the information obtained from the + * tool is integrated into the metadata. + * * `logCategory`: this algorithm uses messagefacility library to emit + * messages to console. This is the category these messages are assigned to. + * + * + */ +class sbn::JobEnvironmentInfoExtractor { + + public: + + /// Configuration record. + struct Config { + + /// Default value of `dropVars`. + static std::vector const DefaultDropVars; + + /// Remove environment variables with names matching these patterns. + std::vector dropVars = DefaultDropVars; + + /// Look for the GIT version of these repositories. + std::vector repositories{ "sbncode" }; + + std::string logCategory = "JobEnvironmentInfoExtractor"; + + // needed because of compiler bugs + // https://bugs.llvm.org/show_bug.cgi?id=36684 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645 + Config() {} + + }; // Config + + + /// Constructor with configuration. + JobEnvironmentInfoExtractor(Config const& config = Config{}); + + /// Extracts all the information from the environment. + sbn::JobEnvironmentInfo extract + (art::ModuleDescription const& moduleInfo); // not guaranteed to be const! + + /** + * @brief Extracts all the information from the environment. + * @param moduleInfo information about the _art_ module being executed + * @return an object with all extracted metadata + */ + sbn::JobEnvironmentInfo operator() (art::ModuleDescription const& moduleInfo) + { return extract(moduleInfo); } + + // --- BEGIN --- Partial metadata extraction ------------------------------- + /** + * @name Partial metadata extraction + * + * The member functions in this group allow the extraction of part of the job + * metadata. The function `extract()` includes the information from all of + * them, plus some more. + */ + /// @{ + + /// Returns the environment variables. + sbn::OrderedPairList extractEnvironmentVariables() const; + + /// Returns the version of the source repositories. + sbn::OrderedPairList extractSourceVersions() const; + + /// @} + // --- END ----- Partial metadata extraction ------------------------------- + + private: + + std::vector fDropVars; ///< Remove variables with matching names. + + ///< Names of the repositories to ask for a version report to. + std::vector fRepositoryReports; + + std::string fLogCategory; + + /// Converts all the patterns into regular expression objects. + static std::vector prepareRegEx + (std::vector const& patterns); + + /// @brief Returns the index of the first pattern `value` matches. + /// @return index of the first matched pattern, `patterns.size()` if none + static std::size_t matchPatterns + (std::string const& value, std::vector const& patterns); + +}; // sbn::JobEnvironmentInfoExtractor + + +// ----------------------------------------------------------------------------- + +#endif // SBNCODE_METADATA_JOBENVIRONMENTINFOEXTRACTOR_H diff --git a/sbncode/Metadata/MetadataSBN.cc b/sbncode/Metadata/MetadataSBN.cc new file mode 100644 index 000000000..0cb7d5f11 --- /dev/null +++ b/sbncode/Metadata/MetadataSBN.cc @@ -0,0 +1,425 @@ +//////////////////////////////////////////////////////////////////////// +// Name: MetadataSBN.cc. +// +// Purpose: generate SBN-specific sam metadata for root Tfiles (histogram or ntuple files). +// +// FCL parameters: Experiment: Experiment name ("sbnd" or "icarus"). +// JSONFileName: Name of generated .json file(s). +// dataTier: Data tier(s). +// for ntuples, dataTier = root-tuple; +// for histos, dataTier = root-histogram +// (default value: root-tuple) +// fileFormat: This is currently specified by the user, +// the fileFormat for Tfiles is "root" (default value: root) +// Merge: Merge flag. +// 1 - Set merge.merge = 1 and merge.merged = 0 +// 0 - Set merge.merge = 0 and merge.merged = 0 +// -1 - Do not generate merge parameters. +// POTModuleLabel - POTSummary module label (default "generator"). +// +// Parameters JSONFileName, dataTier, and fileFormat can be single +// stringss or sequences of strings. In case of sequences of length +// greater than one, multiple json files will be generated. Sequences +// must be equal length. +// +// Other notes: 1. This service uses the ART's standard file_catalog_metadata service +// to extract some of the common (common to both ART and TFile outputs) +// job-specific metadata parameters, so, it is important to call this +// service in your fcl file +// stick this line in your "services" section of fcl file: +// FileCatalogMetadata: @local::art_file_catalog_mc +// +// 2. When you call FileCatalogMetadata service in your fcl file, and if +// you have (art) root Output section in your fcl file, and if you do not +// have "dataTier" specified in that section, then this service will throw +// an exception. To avoid this, either remove the entire root Output section +// in your fcl file (and remove art stream output from your end_paths) or +// include appropriate dataTier information in the section.If you are only +// running analysis job, best way is to not include any art root Output section. +// +// 3. This service is exclusively written to work with production (in other +// words for jobs submitted through grid). Some of the metadata parameters +// (output TFileName, filesize, Project related details) are captured/updated +// during and/or after the workflow. +// +// +// Created: 21-Feb-2018, D. Brailsford +// based on the SBND version by T. Junk which is based on the +// based on the MicroBooNE example by S. Gollapinni +// +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sbncode/Metadata/MetadataSBN.h" +#include "sbncode/Metadata/FileCatalogMetadataSBN.h" +#include "larcoreobj/SummaryData/POTSummary.h" + +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/SubRun.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "art/Framework/Services/System/FileCatalogMetadata.h" +#include "art/Framework/Services/System/TriggerNamesService.h" +#include "art/Utilities/OutputFileInfo.h" +#include "cetlib_except/exception.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +#include "TROOT.h" +#include "TFile.h" +#include "TTimeStamp.h" + +using namespace std; + +//-------------------------------------------------------------------- + +// Constructor. +util::MetadataSBN::MetadataSBN(fhicl::ParameterSet const& pset, + art::ActivityRegistry& reg): + fFileStats{"", art::ServiceHandle{}->getProcessName()} +{ + // Insist on configuring Experiment from the fcl file (ideally) or the + // environment. + const char* expt = getenv("EXPERIMENT"); + if(expt) fExperiment = pset.get("Experiment", expt); else fExperiment = pset.get("Experiment"); + std::transform(fExperiment.begin(), fExperiment.end(), fExperiment.begin(), [](unsigned char c){return std::tolower(c);}); + + // Get scalar/vector parameters. + if(pset.is_key_to_atom("JSONFileName")) + fJSONFileName.push_back(pset.get("JSONFileName")); + else if(pset.is_key_to_sequence("JSONFileName")) + fJSONFileName = pset.get >("JSONFileName"); + + if(pset.is_key_to_atom("dataTier")) + fDataTier.push_back(pset.get("dataTier")); + else if(pset.is_key_to_sequence("dataTier")) + fDataTier = pset.get >("dataTier"); + if(fDataTier.size() != fJSONFileName.size()) + throw cet::exception("MetadataSBN") << "FCL sequence size mismatch.\n"; + + if(pset.is_key_to_atom("fileFormat")) + fFileFormat.push_back(pset.get("fileFormat")); + else if(pset.is_key_to_sequence("fileFormat")) + fFileFormat = pset.get >("fileFormat"); + if(fFileFormat.size() != fJSONFileName.size()) + throw cet::exception("MetadataSBN") << "FCL sequence size mismatch.\n"; + + if(pset.has_key("Merge")) { + if(pset.is_key_to_atom("Merge")) + fMerge.push_back(pset.get("Merge")); + else if(pset.is_key_to_sequence("Merge")) + fMerge = pset.get >("Merge"); + } + fPOTModuleLabel = pset.get("POTModuleLabel", "generator"); + + reg.sPostBeginJob.watch(this, &MetadataSBN::postBeginJob); + reg.sPostOpenFile.watch(this, &MetadataSBN::postOpenInputFile); + reg.sPostCloseFile.watch(this, &MetadataSBN::postCloseInputFile); + reg.sPostProcessEvent.watch(this, &MetadataSBN::postEvent); + reg.sPostBeginSubRun.watch(this, &MetadataSBN::postBeginSubRun); + reg.sPostEndSubRun.watch(this, &MetadataSBN::postEndSubRun); + + // get metadata from the FileCatalogMetadataSBN service, which is filled on its construction + art::ServiceHandle paramhandle; + md.fFCLName = paramhandle->GetFCLName(); + md.fProjectName = paramhandle->GetProjectName(); + md.fProjectStage = paramhandle->GetProjectStage(); + md.fProjectVersion = paramhandle->GetProjectVersion(); + md.fProjectSoftware = paramhandle->GetProjectSoftware(); + md.fProductionName = paramhandle->GetProductionName(); + md.fProductionType = paramhandle->GetProductionType(); + md.merge = -1; + md.fTotPOT = 0.; +} + +/// Un-quote quoted strings +std::string UnQuoteString(std::string s) +{ + if(s.size() < 2 || s[0] != '\"' || s[s.size()-1] != '\"') return s; + s.erase(0, 1); + s.erase(s.size()-1, 1); + return s; +} + +void MaybeCopyFromMap(const std::map& in, + const std::string& key, + std::string& out) +{ + const auto it = in.find(key); + if(it == in.end()){ + out = ""; + } + else{ + out = UnQuoteString(it->second); + } +} + +void MaybeCopyToMap(const std::string& in, + const std::string& key, + std::map& out) +{ + if(!in.empty()) out[key] = UnQuoteString(in); +} + +//-------------------------------------------------------------------- +// PostBeginJob callback. +// Insert per-job metadata via Metadata service. +void util::MetadataSBN::postBeginJob() +{ + // get the start time + md.fstart_time = time(0); + + // Get art metadata service and extract paramters from there + art::ServiceHandle artmds; + + art::FileCatalogMetadata::collection_type artmd; + artmds->getMetadata(artmd); + + std::map mdmap; + for(const auto& d: artmd) + mdmap[d.first] = UnQuoteString(d.second); + + // if a certain paramter/key is not found, assign an empty string value to it + MaybeCopyFromMap(mdmap, "application.family", std::get<0>(md.fapplication)); + MaybeCopyFromMap(mdmap, "art.process_name", std::get<1>(md.fapplication)); + MaybeCopyFromMap(mdmap, "application.version", std::get<2>(md.fapplication)); + MaybeCopyFromMap(mdmap, "group", md.fgroup); + MaybeCopyFromMap(mdmap, "file_type", md.ffile_type); + MaybeCopyFromMap(mdmap, "art.run_type", frunType); +} + + +//-------------------------------------------------------------------- +// PostOpenFile callback. +void util::MetadataSBN::postOpenInputFile(std::string const& fn) +{ + // save parent input files here + // 08/06 DBrailsford: Only save the parent string if the string is filled. The string still exists (with 0 characters) for generation stage files. See redmine issue 20124 + if (fn.length() > 0) md.fParents.insert(fn); + fFileStats.recordInputFile(fn); +} + +//-------------------------------------------------------------------- +// PostEvent callback. +void util::MetadataSBN::postEvent(art::Event const& evt, art::ScheduleContext) +{ + std::lock_guard lock(fMutex); + + art::RunNumber_t run = evt.run(); + art::SubRunNumber_t subrun = evt.subRun(); + art::EventNumber_t event = evt.event(); + art::SubRunID srid = evt.id().subRunID(); + + // save run, subrun and runType information once every subrun + if (fSubRunNumbers.count(srid) == 0){ + fSubRunNumbers.insert(srid); + md.fruns.push_back(make_tuple(run, subrun, frunType)); + } + + // save the first event + if (md.fevent_count == 0) md.ffirst_event = event; + md.flast_event = event; + // event counter + ++md.fevent_count; + +} + +//-------------------------------------------------------------------- +// PostSubRun callback. +void util::MetadataSBN::postBeginSubRun(art::SubRun const& sr) +{ + std::lock_guard lock(fMutex); + + art::RunNumber_t run = sr.run(); + art::SubRunNumber_t subrun = sr.subRun(); + art::SubRunID srid = sr.id(); + + // save run, subrun and runType information once every subrun + if (fSubRunNumbers.count(srid) == 0){ + fSubRunNumbers.insert(srid); + md.fruns.push_back(make_tuple(run, subrun, frunType)); + } +} + +//-------------------------------------------------------------------- +// PostEndSubRun callback. +void util::MetadataSBN::postEndSubRun(art::SubRun const& sr) +{ + std::lock_guard lock(fMutex); + + art::Handle< sumdata::POTSummary > potListHandle; + double fTotPOT = 0; + if(sr.getByLabel(fPOTModuleLabel,potListHandle)){ + fTotPOT+=potListHandle->totpot; + } + + md.fTotPOT += fTotPOT; +} + + +//-------------------------------------------------------------------- +std::string Escape(const std::string& s) +{ + // If it's formatted as a dict or list, trust it's already formatted + if(s.size() >= 2 && ((s[0] == '{' && s.back() == '}') || (s[0] == '[' && s.back() == ']'))) return s; + + // otherwise quote it + return "\""+s+"\""; +} + +//-------------------------------------------------------------------- +std::string util::MetadataSBN::GetParentsString() const +{ + if(md.fParents.empty()) return ""; + + unsigned int c = 0; + + std::string ret = "[\n"; + for(auto parent: md.fParents) { + std::cout<<"Parent " << c << ": " << parent << std::endl; + c++; + size_t n = parent.find_last_of('/'); + size_t f1 = (n == std::string::npos ? 0 : n+1); + ret += " {\n \"file_name\": \"" + parent.substr(f1) + "\"\n }"; + if(md.fParents.size() == 1 || c == md.fParents.size()) ret += "\n"; + else ret += ",\n"; + } + + ret += " ]"; + return ret; +} + +//-------------------------------------------------------------------- +std::string util::MetadataSBN::GetRunsString() const +{ + unsigned int c = 0; + + std::string ret = "[\n"; + for(auto&t :md.fruns){ + c++; + ret += " [\n " + std::to_string(std::get<0>(t)) + ",\n " + std::to_string(std::get<1>(t)) + ",\n \"" + std::get<2>(t) + "\"\n ]"; + if(md.fruns.size() == 1 || c == md.fruns.size()) ret += "\n"; + else ret += ",\n"; + } + ret += " ]"; + return ret; +} + +//-------------------------------------------------------------------- +void util::MetadataSBN::GetMetadataMaps(std::map& strs, + std::map& ints, + std::map& doubles, + std::map& objs) +{ + strs.clear(); ints.clear(); doubles.clear(); objs.clear(); + + objs["application"] = "{\"family\": \""+std::get<0>(md.fapplication)+"\", \"name\": \""+std::get<1>(md.fapplication)+"\", \"version\": \""+std::get<2>(md.fapplication)+"\"}"; + + if(!md.fParents.empty()) objs["parents"] = GetParentsString(); + if(!md.fruns.empty()) objs["runs"] = GetRunsString(); + + // convert start and end times into time format: Year-Month-DayTHours:Minutes:Seconds + char endbuf[80], startbuf[80]; + struct tm tstruct; + tstruct = *localtime(&md.fend_time); + strftime(endbuf,sizeof(endbuf),"%Y-%m-%dT%H:%M:%S",&tstruct); + tstruct = *localtime(&md.fstart_time); + strftime(startbuf,sizeof(startbuf),"%Y-%m-%dT%H:%M:%S",&tstruct); + + strs["start_time"] = startbuf; + strs["end_time"] = endbuf; + + strs["data_tier"] = md.fdata_tier; + ints["event_count"] = md.fevent_count; + strs["file_format"] = md.ffile_format; + ints["first_event"] = md.ffirst_event; + ints["last_event"] = md.flast_event; + + const std::string proj = fExperiment+"_project"; + MaybeCopyToMap(md.fFCLName, "fcl.name", strs); + MaybeCopyToMap(md.fProjectName, proj+".name", strs); + MaybeCopyToMap(md.fProjectStage, proj+".stage", strs); + MaybeCopyToMap(md.fProjectVersion, proj+".version", strs); + MaybeCopyToMap(md.fProjectSoftware, proj+".software", strs); + MaybeCopyToMap(md.fProductionName, "production.name", strs); + MaybeCopyToMap(md.fProductionType, "production.type", strs); + + MaybeCopyToMap(md.fgroup, "group", strs); + MaybeCopyToMap(md.ffile_type, "file_type", strs); + + if(md.merge >= 0) { + ints["merge.merge"] = (md.merge==0 ? 0 : 1); + ints["merge.merged"] = 0; + } + doubles["mc.pot"] = md.fTotPOT; +} + +//-------------------------------------------------------------------- +// PostCloseFile callback. +void util::MetadataSBN::postCloseInputFile() +{ + //update end time + md.fend_time = time(0); + + // Loop over files. + + for(unsigned int i=0; i i) + md.merge = fMerge[i]; + else + md.merge = -1; + + + std::map strs; + std::map ints; + std::map doubles; + std::map objs; + GetMetadataMaps(strs, ints, doubles, objs); + + // open a json file and write everything from the struct md complying to the + // samweb json format. This json file holds the below information temporarily. + // If you submitted a grid job invoking this service, the information from + // this file is appended to a final json file and this file will be removed + + if(!fJSONFileName[i].empty()){ + std::ofstream jsonfile; + jsonfile.open(fJSONFileName[i]); + jsonfile << "{\n"; + + bool once = true; + for(auto& it: objs){ + if(!once) jsonfile << ",\n"; + once = false; + jsonfile << " \"" << it.first << "\": " << it.second; + } + for(auto& it: strs){ + // Have to escape string outputs + jsonfile << ",\n \"" << it.first << "\": \"" << it.second << "\""; + } + for(auto& it: ints){ + jsonfile << ",\n \"" << it.first << "\": " << it.second; + } + for(auto& it: doubles){ + jsonfile << ",\n \"" << it.first << "\": " << it.second; + } + + jsonfile<<"\n}\n"; + jsonfile.close(); + } + } + + fFileStats.recordFileClose(); + //TODO figure out how to make the name identical to the TFile + //std::string new_name = fRenamer.maybeRenameFile("myjson.json",fJSONFileName); +} diff --git a/sbncode/Metadata/MetadataSBN_service.cc b/sbncode/Metadata/MetadataSBN_service.cc index fed3614ef..e4024421b 100644 --- a/sbncode/Metadata/MetadataSBN_service.cc +++ b/sbncode/Metadata/MetadataSBN_service.cc @@ -1,428 +1,4 @@ -//////////////////////////////////////////////////////////////////////// -// Name: MetadataSBN_service.cc. -// -// Purpose: generate SBN-specific sam metadata for root Tfiles (histogram or ntuple files). -// -// FCL parameters: Experiment: Experiment name ("sbnd" or "icarus"). -// JSONFileName: Name of generated .json file(s). -// dataTier: Data tier(s). -// for ntuples, dataTier = root-tuple; -// for histos, dataTier = root-histogram -// (default value: root-tuple) -// fileFormat: This is currently specified by the user, -// the fileFormat for Tfiles is "root" (default value: root) -// Merge: Merge flag. -// 1 - Set merge.merge = 1 and merge.merged = 0 -// 0 - Set merge.merge = 0 and merge.merged = 0 -// -1 - Do not generate merge parameters. -// POTModuleLabel - POTSummary module label (default "generator"). -// -// Parameters JSONFileName, dataTier, and fileFormat can be single -// stringss or sequences of strings. In case of sequences of length -// greater than one, multiple json files will be generated. Sequences -// must be equal length. -// -// Other notes: 1. This service uses the ART's standard file_catalog_metadata service -// to extract some of the common (common to both ART and TFile outputs) -// job-specific metadata parameters, so, it is important to call this -// service in your fcl file -// stick this line in your "services" section of fcl file: -// FileCatalogMetadata: @local::art_file_catalog_mc -// -// 2. When you call FileCatalogMetadata service in your fcl file, and if -// you have (art) root Output section in your fcl file, and if you do not -// have "dataTier" specified in that section, then this service will throw -// an exception. To avoid this, either remove the entire root Output section -// in your fcl file (and remove art stream output from your end_paths) or -// include appropriate dataTier information in the section.If you are only -// running analysis job, best way is to not include any art root Output section. -// -// 3. This service is exclusively written to work with production (in other -// words for jobs submitted through grid). Some of the metadata parameters -// (output TFileName, filesize, Project related details) are captured/updated -// during and/or after the workflow. -// -// -// Created: 21-Feb-2018, D. Brailsford -// based on the SBND version by T. Junk which is based on the -// based on the MicroBooNE example by S. Gollapinni -// -//////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sbncode/Metadata/MetadataSBN.h" -#include "sbncode/Metadata/FileCatalogMetadataSBN.h" -#include "larcoreobj/SummaryData/POTSummary.h" - -#include "art/Framework/Principal/Event.h" -#include "art/Framework/Principal/SubRun.h" -#include "art/Framework/Services/Registry/ServiceHandle.h" #include "art/Framework/Services/Registry/ServiceDefinitionMacros.h" -#include "art/Framework/Services/System/FileCatalogMetadata.h" -#include "art/Framework/Services/System/TriggerNamesService.h" -#include "art/Utilities/OutputFileInfo.h" -#include "cetlib_except/exception.h" -#include "messagefacility/MessageLogger/MessageLogger.h" - -#include "TROOT.h" -#include "TFile.h" -#include "TTimeStamp.h" - -using namespace std; - -//-------------------------------------------------------------------- - -// Constructor. -util::MetadataSBN::MetadataSBN(fhicl::ParameterSet const& pset, - art::ActivityRegistry& reg): - fFileStats{"", art::ServiceHandle{}->getProcessName()} -{ - // Insist on configuring Experiment from the fcl file (ideally) or the - // environment. - const char* expt = getenv("EXPERIMENT"); - if(expt) fExperiment = pset.get("Experiment", expt); else fExperiment = pset.get("Experiment"); - std::transform(fExperiment.begin(), fExperiment.end(), fExperiment.begin(), [](unsigned char c){return std::tolower(c);}); - - // Get scalar/vector parameters. - if(pset.is_key_to_atom("JSONFileName")) - fJSONFileName.push_back(pset.get("JSONFileName")); - else if(pset.is_key_to_sequence("JSONFileName")) - fJSONFileName = pset.get >("JSONFileName"); - - if(pset.is_key_to_atom("dataTier")) - fDataTier.push_back(pset.get("dataTier")); - else if(pset.is_key_to_sequence("dataTier")) - fDataTier = pset.get >("dataTier"); - if(fDataTier.size() != fJSONFileName.size()) - throw cet::exception("MetadataSBN") << "FCL sequence size mismatch.\n"; - - if(pset.is_key_to_atom("fileFormat")) - fFileFormat.push_back(pset.get("fileFormat")); - else if(pset.is_key_to_sequence("fileFormat")) - fFileFormat = pset.get >("fileFormat"); - if(fFileFormat.size() != fJSONFileName.size()) - throw cet::exception("MetadataSBN") << "FCL sequence size mismatch.\n"; - - if(pset.has_key("Merge")) { - if(pset.is_key_to_atom("Merge")) - fMerge.push_back(pset.get("Merge")); - else if(pset.is_key_to_sequence("Merge")) - fMerge = pset.get >("Merge"); - } - fPOTModuleLabel = pset.get("POTModuleLabel", "generator"); - - reg.sPostBeginJob.watch(this, &MetadataSBN::postBeginJob); - reg.sPostOpenFile.watch(this, &MetadataSBN::postOpenInputFile); - reg.sPostCloseFile.watch(this, &MetadataSBN::postCloseInputFile); - reg.sPostProcessEvent.watch(this, &MetadataSBN::postEvent); - reg.sPostBeginSubRun.watch(this, &MetadataSBN::postBeginSubRun); - reg.sPostEndSubRun.watch(this, &MetadataSBN::postEndSubRun); - - // get metadata from the FileCatalogMetadataSBN service, which is filled on its construction - art::ServiceHandle paramhandle; - md.fFCLName = paramhandle->GetFCLName(); - md.fProjectName = paramhandle->GetProjectName(); - md.fProjectStage = paramhandle->GetProjectStage(); - md.fProjectVersion = paramhandle->GetProjectVersion(); - md.fProjectSoftware = paramhandle->GetProjectSoftware(); - md.fProductionName = paramhandle->GetProductionName(); - md.fProductionType = paramhandle->GetProductionType(); - md.merge = -1; - md.fTotPOT = 0.; -} - -/// Un-quote quoted strings -std::string UnQuoteString(std::string s) -{ - if(s.size() < 2 || s[0] != '\"' || s[s.size()-1] != '\"') return s; - s.erase(0, 1); - s.erase(s.size()-1, 1); - return s; -} - -void MaybeCopyFromMap(const std::map& in, - const std::string& key, - std::string& out) -{ - const auto it = in.find(key); - if(it == in.end()){ - out = ""; - } - else{ - out = UnQuoteString(it->second); - } -} - -void MaybeCopyToMap(const std::string& in, - const std::string& key, - std::map& out) -{ - if(!in.empty()) out[key] = UnQuoteString(in); -} - -//-------------------------------------------------------------------- -// PostBeginJob callback. -// Insert per-job metadata via Metadata service. -void util::MetadataSBN::postBeginJob() -{ - // get the start time - md.fstart_time = time(0); - - // Get art metadata service and extract paramters from there - art::ServiceHandle artmds; - - art::FileCatalogMetadata::collection_type artmd; - artmds->getMetadata(artmd); - - std::map mdmap; - for(const auto& d: artmd) - mdmap[d.first] = UnQuoteString(d.second); - - // if a certain paramter/key is not found, assign an empty string value to it - MaybeCopyFromMap(mdmap, "application.family", std::get<0>(md.fapplication)); - MaybeCopyFromMap(mdmap, "art.process_name", std::get<1>(md.fapplication)); - MaybeCopyFromMap(mdmap, "application.version", std::get<2>(md.fapplication)); - MaybeCopyFromMap(mdmap, "group", md.fgroup); - MaybeCopyFromMap(mdmap, "file_type", md.ffile_type); - MaybeCopyFromMap(mdmap, "art.run_type", frunType); -} - - -//-------------------------------------------------------------------- -// PostOpenFile callback. -void util::MetadataSBN::postOpenInputFile(std::string const& fn) -{ - // save parent input files here - // 08/06 DBrailsford: Only save the parent string if the string is filled. The string still exists (with 0 characters) for generation stage files. See redmine issue 20124 - if (fn.length() > 0) md.fParents.insert(fn); - fFileStats.recordInputFile(fn); -} - -//-------------------------------------------------------------------- -// PostEvent callback. -void util::MetadataSBN::postEvent(art::Event const& evt, art::ScheduleContext) -{ - std::lock_guard lock(fMutex); - - art::RunNumber_t run = evt.run(); - art::SubRunNumber_t subrun = evt.subRun(); - art::EventNumber_t event = evt.event(); - art::SubRunID srid = evt.id().subRunID(); - - // save run, subrun and runType information once every subrun - if (fSubRunNumbers.count(srid) == 0){ - fSubRunNumbers.insert(srid); - md.fruns.push_back(make_tuple(run, subrun, frunType)); - } - - // save the first event - if (md.fevent_count == 0) md.ffirst_event = event; - md.flast_event = event; - // event counter - ++md.fevent_count; - -} - -//-------------------------------------------------------------------- -// PostSubRun callback. -void util::MetadataSBN::postBeginSubRun(art::SubRun const& sr) -{ - std::lock_guard lock(fMutex); - - art::RunNumber_t run = sr.run(); - art::SubRunNumber_t subrun = sr.subRun(); - art::SubRunID srid = sr.id(); - - // save run, subrun and runType information once every subrun - if (fSubRunNumbers.count(srid) == 0){ - fSubRunNumbers.insert(srid); - md.fruns.push_back(make_tuple(run, subrun, frunType)); - } -} - -//-------------------------------------------------------------------- -// PostEndSubRun callback. -void util::MetadataSBN::postEndSubRun(art::SubRun const& sr) -{ - std::lock_guard lock(fMutex); - - art::Handle< sumdata::POTSummary > potListHandle; - double fTotPOT = 0; - if(sr.getByLabel(fPOTModuleLabel,potListHandle)){ - fTotPOT+=potListHandle->totpot; - } - - md.fTotPOT += fTotPOT; -} - - -//-------------------------------------------------------------------- -std::string Escape(const std::string& s) -{ - // If it's formatted as a dict or list, trust it's already formatted - if(s.size() >= 2 && ((s[0] == '{' && s.back() == '}') || (s[0] == '[' && s.back() == ']'))) return s; - - // otherwise quote it - return "\""+s+"\""; -} - -//-------------------------------------------------------------------- -std::string util::MetadataSBN::GetParentsString() const -{ - if(md.fParents.empty()) return ""; - - unsigned int c = 0; - - std::string ret = "[\n"; - for(auto parent: md.fParents) { - std::cout<<"Parent " << c << ": " << parent << std::endl; - c++; - size_t n = parent.find_last_of('/'); - size_t f1 = (n == std::string::npos ? 0 : n+1); - ret += " {\n \"file_name\": \"" + parent.substr(f1) + "\"\n }"; - if(md.fParents.size() == 1 || c == md.fParents.size()) ret += "\n"; - else ret += ",\n"; - } - - ret += " ]"; - return ret; -} - -//-------------------------------------------------------------------- -std::string util::MetadataSBN::GetRunsString() const -{ - unsigned int c = 0; - - std::string ret = "[\n"; - for(auto&t :md.fruns){ - c++; - ret += " [\n " + std::to_string(std::get<0>(t)) + ",\n " + std::to_string(std::get<1>(t)) + ",\n \"" + std::get<2>(t) + "\"\n ]"; - if(md.fruns.size() == 1 || c == md.fruns.size()) ret += "\n"; - else ret += ",\n"; - } - ret += " ]"; - return ret; -} - -//-------------------------------------------------------------------- -void util::MetadataSBN::GetMetadataMaps(std::map& strs, - std::map& ints, - std::map& doubles, - std::map& objs) -{ - strs.clear(); ints.clear(); doubles.clear(); objs.clear(); - - objs["application"] = "{\"family\": \""+std::get<0>(md.fapplication)+"\", \"name\": \""+std::get<1>(md.fapplication)+"\", \"version\": \""+std::get<2>(md.fapplication)+"\"}"; - - if(!md.fParents.empty()) objs["parents"] = GetParentsString(); - if(!md.fruns.empty()) objs["runs"] = GetRunsString(); - - // convert start and end times into time format: Year-Month-DayTHours:Minutes:Seconds - char endbuf[80], startbuf[80]; - struct tm tstruct; - tstruct = *localtime(&md.fend_time); - strftime(endbuf,sizeof(endbuf),"%Y-%m-%dT%H:%M:%S",&tstruct); - tstruct = *localtime(&md.fstart_time); - strftime(startbuf,sizeof(startbuf),"%Y-%m-%dT%H:%M:%S",&tstruct); - - strs["start_time"] = startbuf; - strs["end_time"] = endbuf; - - strs["data_tier"] = md.fdata_tier; - ints["event_count"] = md.fevent_count; - strs["file_format"] = md.ffile_format; - ints["first_event"] = md.ffirst_event; - ints["last_event"] = md.flast_event; - - const std::string proj = fExperiment+"_project"; - MaybeCopyToMap(md.fFCLName, "fcl.name", strs); - MaybeCopyToMap(md.fProjectName, proj+".name", strs); - MaybeCopyToMap(md.fProjectStage, proj+".stage", strs); - MaybeCopyToMap(md.fProjectVersion, proj+".version", strs); - MaybeCopyToMap(md.fProjectSoftware, proj+".software", strs); - MaybeCopyToMap(md.fProductionName, "production.name", strs); - MaybeCopyToMap(md.fProductionType, "production.type", strs); - - MaybeCopyToMap(md.fgroup, "group", strs); - MaybeCopyToMap(md.ffile_type, "file_type", strs); - - if(md.merge >= 0) { - ints["merge.merge"] = (md.merge==0 ? 0 : 1); - ints["merge.merged"] = 0; - } - doubles["mc.pot"] = md.fTotPOT; -} - -//-------------------------------------------------------------------- -// PostCloseFile callback. -void util::MetadataSBN::postCloseInputFile() -{ - //update end time - md.fend_time = time(0); - - // Loop over files. - - for(unsigned int i=0; i i) - md.merge = fMerge[i]; - else - md.merge = -1; - - - std::map strs; - std::map ints; - std::map doubles; - std::map objs; - GetMetadataMaps(strs, ints, doubles, objs); - - // open a json file and write everything from the struct md complying to the - // samweb json format. This json file holds the below information temporarily. - // If you submitted a grid job invoking this service, the information from - // this file is appended to a final json file and this file will be removed - - if(!fJSONFileName[i].empty()){ - std::ofstream jsonfile; - jsonfile.open(fJSONFileName[i]); - jsonfile << "{\n"; - - bool once = true; - for(auto& it: objs){ - if(!once) jsonfile << ",\n"; - once = false; - jsonfile << " \"" << it.first << "\": " << it.second; - } - for(auto& it: strs){ - // Have to escape string outputs - jsonfile << ",\n \"" << it.first << "\": \"" << it.second << "\""; - } - for(auto& it: ints){ - jsonfile << ",\n \"" << it.first << "\": " << it.second; - } - for(auto& it: doubles){ - jsonfile << ",\n \"" << it.first << "\": " << it.second; - } - - jsonfile<<"\n}\n"; - jsonfile.close(); - } - } - - fFileStats.recordFileClose(); - //TODO figure out how to make the name identical to the TFile - //std::string new_name = fRenamer.maybeRenameFile("myjson.json",fJSONFileName); -} +#include "sbncode/Metadata/MetadataSBN.h" DEFINE_ART_SERVICE(util::MetadataSBN) diff --git a/sbncode/Metadata/RepositoryVersionReportUtils.cpp b/sbncode/Metadata/RepositoryVersionReportUtils.cpp new file mode 100644 index 000000000..5e9665657 --- /dev/null +++ b/sbncode/Metadata/RepositoryVersionReportUtils.cpp @@ -0,0 +1,71 @@ +/** + * @file sbncode/Metadata/RepositoryVersionReportUtils.cxx + * @brief Interface for _art_ tool reporting the version of packages. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * @see sbncode/Metadata/RepositoryVersionReportUtils.h + */ + +// library header +#include "sbncode/Metadata/RepositoryVersionReportUtils.h" + +// SBN libraries +#include "sbncode/Metadata/RepositoryVersionReporter.h" + +// framework libraries +#include "art/Utilities/make_tool.h" +#include "canvas/Utilities/Exception.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "fhiclcpp/ParameterSet.h" + +// C/C++ standard library +#include // std::unique_ptr<> + + +// ----------------------------------------------------------------------------- +bool sbn::addVersionFromRepository + (sbn::OrderedPairList& versionList, std::string const& repoName) +{ + + std::string const toolName = repoName + "RepositoryVersion"; + + fhicl::ParameterSet config; + config.put("tool_type", toolName); + + std::unique_ptr reportTool; + try { + reportTool = art::make_tool(config); + } + catch(cet::exception const& e) { + mf::LogWarning{ "RepositoryVersionReporter" } + << "No report from repository '" << repoName << "': tool not found."; + mf::LogDebug{ "RepositoryVersionReporter" } << "Error:\n" << e; + return false; + } + + // blindly add everything + versionList.items.insert + (versionList.items.end(), reportTool->begin(), reportTool->end()); + + return true; + +} // sbn::addVersionFromRepository() + + +// ----------------------------------------------------------------------------- +std::vector sbn::addVersionFromRepositories( + sbn::OrderedPairList& versionList, std::vector const& repoNames +) { + + std::vector missingRepos; + for (std::string const& repoName: repoNames) { + if (addVersionFromRepository(versionList, repoName)) continue; + missingRepos.push_back(repoName); + } + + return repoNames; + +} // sbn::addVersionFromRepositories() + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/Metadata/RepositoryVersionReportUtils.h b/sbncode/Metadata/RepositoryVersionReportUtils.h new file mode 100644 index 000000000..1c7cea867 --- /dev/null +++ b/sbncode/Metadata/RepositoryVersionReportUtils.h @@ -0,0 +1,60 @@ +/** + * @file sbncode/Metadata/RepositoryVersionReportUtils.h + * @brief Interface for _art_ tool reporting the version of packages. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * @see sbncode/Metadata/RepositoryVersionReportUtils.cxx + */ + +#ifndef SBNCODE_METADATA_REPOSITORYVERSIONREPORTUTILS_H +#define SBNCODE_METADATA_REPOSITORYVERSIONREPORTUTILS_H + + +// SBN libraries +#include "sbnobj/Common/Metadata/OrderedPairList.h" + +// C/C++ standard library +#include +#include + + +// ----------------------------------------------------------------------------- +namespace sbn { + + /** + * @brief Adds versions from the specified repository. + * @param[out] versionList version list to add to + * @param repoName name of the repository to be queried + * @return whether the repository plugin could be loaded + * + * The plugin with name `repoName + "RepositoryVersion"` is loaded and its + * report is appended to `versionList`. + * It will be necessary to call `versionList.finish()` after all the additions + * are done. + * + * This utility is used in the + * @ref SBNsourceMetadataSystem "repository version tracking system". + */ + bool addVersionFromRepository + (sbn::OrderedPairList& versionList, std::string const& repoName); + + + /** + * @brief Adds versions from the specified repositories. + * @param[out] versionList version list to add to + * @param repoNames name of the repositories to be queried + * @return which repositories which could not be reached + * @see addVersionFromRepository + * + * This is equivalent to repeatedly calling `addVersionFromRepository()`. + */ + std::vector addVersionFromRepositories( + sbn::OrderedPairList& versionList, std::vector const& repoNames + ); + +} // namespace sbn + + +// ----------------------------------------------------------------------------- + +#endif // SBNCODE_METADATA_REPOSITORYVERSIONREPORTUTILS_H diff --git a/sbncode/Metadata/RepositoryVersionReporter.h b/sbncode/Metadata/RepositoryVersionReporter.h new file mode 100644 index 000000000..55e2e05bf --- /dev/null +++ b/sbncode/Metadata/RepositoryVersionReporter.h @@ -0,0 +1,75 @@ +/** + * @file sbncode/Metadata/RepositoryVersionReporter.h + * @brief Interface for _art_ tool reporting the version of packages. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * + */ + +#ifndef SBNCODE_METADATA_REPOSITORYVERSIONREPORTER_H +#define SBNCODE_METADATA_REPOSITORYVERSIONREPORTER_H + + +// SBN libraries +#include "sbnobj/Common/Metadata/OrderedPairList.h" + +// C/C++ standard library +#include // std::distance() + + +// ----------------------------------------------------------------------------- +namespace sbn { class RepositoryVersionReporter; } +/** + * @brief Interface for _art_ tool reporting the version of packages. + * + * An object reporting package versions will return them as a range of pairs + * package name/package version, each element being a string. + * + * This interface is not at all generic, and the iterators that are returned + * are exactly `const_iterator` ones defined in this interface, which are + * iterators of the collection `vector_t` also defined here, which is exactly + * a `std::vector`. + * + * @note At this point there is no polymorphic interface: derived classes will + * fetch their data at construction time and keep it for anybody curious + * to learn it. In the future an actual virtual interface could become + * necessary, and at that point it would be clearer what it should + * contain. + * + */ +struct sbn::RepositoryVersionReporter { + + /// Type of the name of a package (a `std::string`). + using PackageName_t = sbn::OrderedPairList::Key_t; + + /// Type of the version of a package (a `std::string`). + using PackegaVersion_t = sbn::OrderedPairList::Value_t; + + // standard C++ container types + using value_type = sbn::OrderedPairList::value_type; + using size_type = sbn::OrderedPairList::size_type; + using const_iterator = sbn::OrderedPairList::const_iterator; + + + /// Collected packages and their versions. + sbn::OrderedPairList packageVersions; + + + /// Begin-iterator for the package-version pairs. + const_iterator begin() const { return packageVersions.begin(); } + + /// End-iterator for the package-version pairs. + const_iterator end() const { return packageVersions.end(); } + + /// Returns whether there is no entry in the report. + bool empty() const noexcept { return begin() == end(); } + + /// Returns the number of items in the report. + size_type size() const { return std::distance(begin(), end()); } + +}; // sbn::RepositoryVersionReporter + + +// ----------------------------------------------------------------------------- + +#endif // SBNCODE_METADATA_REPOSITORYVERSIONREPORTER_H diff --git a/sbncode/Metadata/SBNsourceMetadataSystem.dox b/sbncode/Metadata/SBNsourceMetadataSystem.dox new file mode 100644 index 000000000..eb56ef233 --- /dev/null +++ b/sbncode/Metadata/SBNsourceMetadataSystem.dox @@ -0,0 +1,367 @@ +/** @page SBNsourceMetadataSystem Source code and execution environment metadata system in SBN code +@author Gianluca Petrillo (petrillo@slac.stanford.edu) + + +At highest level, the plugin `SaveJobEnvironment` allows storage of metadata +into the _art_/ROOT and `TFileService` files, and the output module +`DumpJobEnvironment` prints the information saved in a _art_/ROOT file on +console. + +The system to achieve that result is quite convoluted, and this documentation +describes it in detail. + + +Which information is extracted +=============================== + +The extraction algorithm, `sbn::JobEnvironmentInfoExtractor`, includes +information ("metadata") about: + * The execution environment of the job: + * the complete dump of environment variables (except the ones identified as + shell functions); when using UPS, this can be used to track the version + of the products (UPS packages) set up during the job. + * The GIT version of the repositories used during the build process of the + code for some selected source code repositories. + * Information from the software being executed: + * _art_ process name; + * _art_ version. + +This information is extracted for each _art_ process; since typically each final +output file is the result of a sequence of jobs, the output files will contain +multiple metadata sets, for each of the input and job that ended into the file +data. + + +How the information is stored for later use +============================================ + +The goal of the system is to save the metadata into each output file, so that it +is always known wherever the data is. + +Our current data system includes four output carriers: + 1. _art_/ROOT files, managed directly by _art_ and with a rigid format; + 2. `TFileService` supplemental ROOT file, also managed by _art_ but with a + format that is freer; + 3. LArCV files shipped to SPINE reconstruction framework; + 4. Common Analysis Format (CAF), managed by SBN via a _art_ module. + +At the moment of writing (at the inception of this system), the metadata is only +written into the _art_/ROOT and the `TFileService` output files. + +In addition, the system needs a way to store some of the information that would +not be available at run time: an example is the version of the software included +in the build, which is usually a GIT tag that does not survive to the +distributed binary code. +The options of where to keep this information include: as a executable code +(library) that software can link to and call/read; FHiCL configuration that jobs +can include; simple text files that jobs can read. + +There are limitations and shortcomings in each of the options. +The choice of this system was to include the version into an executable library. +The reason why the text file was not chosen is that it requires a convention of +where the file can be found, which is not trivial given the relocation of +distributed code. +The reason why FHiCL configuration was not preferred is that only _art_/ROOT +files automatically include that information. In addition, each job +configuration would need to know _exactly_ which repositories are being included, +since FHiCL parser does not tolerate missing information. The first limitation +can be worked around, for example with the experimental `icaruscode` module +`SaveConfigurationIntoTFile`, which was never fully completed or tested. +The selected option, library code, has ways to address these issues. There is +already a tracking system in place for the libraries, and _art_ has a "factory" +system that can find by name libraries (plug-ins) to be loaded dynamically. +As for the comparison with FHiCL, the code can be configured to look for a large +number of repository libraries, and if one is missing, the resulting error +(for example a thrown C++ exception) can be programmatically caught and handled. +The limitation of this option will be discussed later in this section. + +The difficulty of this system is that it needs to connect very heterogeneous +information sources, which are never all available at the same time. +The source repository is available only at build stage, while the execution +environment is present only... well, during job execution. +The strategy is then to create one shared library for each repository, with +a standard name (`RepositoryVersion_\`), with the repository +version information, and to have some modular software that can read these +libraries, collect their information in a single place and then write it. +This is how it was implemented for the single repository named : + 1. CMake discovers the name of the repository from its remote source + (`git remote origin get-url`). + 2. CMake executes a `git describe` call (`git` executable must be available). + 3. CMake generates a C++ header and source file via `configure_file()` starting + from two template files (`GITrepoVersion.{h,cxx}.in`), and creates the + source files `RepositoryVersion_.{h,cxx}`. + 4. CMake creates the local directory library as usual + (e.g. `art_make_library`), which will include the two generated files too. + 5. The two generated files are added to the list of files to install. + +This happens in the source directory that is going to be hosting the library, by +convention `/Metadata` (e.g. in `sbncode/Metadata/CMakeLists.txt`). +The first three steps are collected in macros saved in `SBNutils.cmake`, which +must be included. + +A relevant question with this method is where the `SBNutils.cmake` macro library +should be hosted. The chosen repository must be at the root of the dependencies +of all the repositories that we need to version, but there is not such a root: +`sbnobj` (hosting _art_/ROOT data products) depends only on `lardataobj`, and +`sbnanaobj` (CAF data objects) depends only on ROOT (so, ironically, the root +dependency in our source tree _is_ in fact ROOT, but unfortunately it does not +make sense to add this infrastructure in there). The practical workaround to +this riddle is to duplicate the macro file, with obvious maintenance +consequences. + +@note An **important limitation** of this system is that the information is + extracted by CMake, when the build system decides to run CMake and when + CMake decides to (re)generate the files -- which may be never: at the + moment of writing, the details are not yet clear. + To make sure that the GIT versions are correct, the only safe way is to + zap the area (`mrb z`) and build everything from scratch. This is always + a good idea anyway once the code is ready for prime time. + Again, remember: after code is committed to GIT, an integral rebuild is + likely needed for the code version metadata to reflect the new repository + status. + +@note The presence of an actual library do be dynamically linked to is necessary + and can't be replaced by inline definitions in the header files. + The reason is that the system needs to support some level of delegation + (see the next section) where a package needs to provide information of + a different one which can't on its own. For example, `sbnobj` can extract + and store its own version, but it lacks the dependencies necessary to + provide it to _art_. Therefore, `sbncode` will be in charge of saving + `sbnobj` metadata in addition to its own. Given this structure, if + `sbnobj` version is stored in a header, `sbncode` will learn it at compile + time and keep it hard-coded. It is possible though that the job loads an + updated `sbnobj` instead of the one used during `sbncode` build, in which + case `sbncode` would still report the version hard-coded at the time of + its own build. + This practice is prone to many issues (the scenario described here + outlines a common one) and should be avoided; however, the metadata system + implemented here is a diagnostic tools and as such should be designed to + be more resilient than average. + + +How to arrange a repository with a version library +--------------------------------------------------- + +1. `SBNutils.cmake` needs to be included. It is stored, willy-nilly, in + `sbnobj`. The most obvious option is to add things into the main + `CMakeLists.txt` file of the repository; namely: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + find_package( sbnobj REQUIRED ) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if the `find_package` section not present already, and + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + include(SBNutils) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + just after the inclusion of CETmodules/_art_ macro files and definitely + _before_ the `add_subdirectory()` calls. +2. Check the directory `/Metadata`. + 1. If it does not exist or does not contain a `CMakeLists.txt`: + 1. create it, and add the line + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + add_subdirectory(Metadata) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + to the `/CMakeLists.txt` (in the same directory where + the new `Metadata` directory is); + 2. copy into there from `sbnobj/Metadata` the file `CMakeLists.txt`, + which should be portable enough; or add lines as below. + 2. If it does exist, add to its `CMakeLists.txt`: + 1. at the top of the file (before the build commands): + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + GenerateRepoVersionSource(${CMAKE_PROJECT_NAME}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + which calls the source generation macro from SBNutils; + 2. at the end add to the usual `install_Xxxx()` calls these ones: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + install_headers(LIST "${CMAKE_CURRENT_BINARY_DIR}/${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.h") + install_source(LIST "${CMAKE_CURRENT_BINARY_DIR}/${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.cxx") + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + which add the `.cxx` and `.h` files to the files to install; + 3. make sure that there is a `art_make_library()` call or equivalent + build command in the file; if there is none, or if there are only + plugin or dictionary build calls (`e.g. `cet_build_plugin()`), add + explicitly a + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + art_make_library() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + or a more explicit (and _art_-independent) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + cet_make_library( + SOURCE + "${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.h" + "${${CMAKE_PROJECT_NAME}_GIT_REPO_VERSION_SOURCE}.cxx" + ) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + which will create a library with only the source file generated here. +3. Copy into there from `sbnobj/Metadata` the files `GITrepoVersion.{h,cxx}.in`. + Or write some custom ones (but remember that the _art_ tool needs the + implementation details). + +Note that in principle every `${CMAKE_PROJECT_NAME}` above can be replaced with +the actual CMake project name, hard-coded (e.g. `sbncode`). The only drawback is +that such lines need a further editing when copied into other repositories. + + + +How the information is extracted and serialized in the output +============================================================== + +When a job is executed, the version of all the repositories should ideally be +loaded and then saved into all the relevant output files. +The strategy for _art_ jobs (`lar`) is no less complex than the one described in +the previous paragraph. The choice was to save the information into a _art_ data +product. +Among the complications: + * The data product should be stored to have the least possible duplication. + * Metadata from the input files needs to be propagated into the output files; + and there can be multiple input files, multiple output files, and not in a + one-to-one correspondence. + +This is the general strategy: + + 1. Metadata is stored into a _results-level data product_, which is an + output-file-level data product (as opposed to run-level, subrun-level or + event-level) bound to the output module (e.g. `RootOutput`). + The class of the data product is defined in `sbnobj`. + 2. A plugin will take care of writing the metadata as a data product. This + plugin is specific to output modules like `RootOutput` and is somehow + different, but similar, to _art_ modules. The plugin has also the task of + attempting to collect the relevant information from all the possible + source repositories, as listed in the FHiCL configuration of the plugin. + If the information from a source repository is not available, that source is + simply not included in the metadata. The output plugin is defined in + `sbncode`, which is the lowest _art_-aware repository under our control. + 3. The plugin, called `SaveJobEnvironment`, calls an extractor utility, + `sbn::JobEnvironmentInfoExtractor`, which utilizes _art_ tools to + dynamically load information from all the source repositories. + Tools have conventional names (`RepositoryVersion`). + 4. Each tool is given the chance to provide arbitrary metadata, but they are + expected to fill in with the name and version of their repository. + +The factorization of the extractor utility from the output plugin allows to use +the utility also on other contexts, for example when running CAF maker. However, +because of the use of the _art_ tool infrastructure, it stills requires _art_ to +be functional. In addition, some of the information is extracted from _art_ +itself (e.g. the process name), so _art_ is also necessary as information source +if the full information is desired. + +A limitation of the _art_-tool-based strategy is that the repository containing +the tool must depend on _art_. Several source repositories do not fulfil this +requirement, by design; e.g. `sbnobj` and `icarusalg`. These repositories still +need to be instrumented with CMake instructions to generate the repository +version library (repositories which do not create libraries are very rare), and +one of the plugins of the dependent repositories must be in charge of reading +the version of these repositories together with their own. So for example +`sbnobj` will generate a `sbnobj_Metadata` library, and _art_ tool +`sbnobjRepositoryVersion` in `sbncode` will link to both `sbncode::Metadata` +for the version of `sbncode`, and to `sbnobj::Metadata` for the version of +non-art-aware repository it depends on, `sbnobj`. Since the dependencies of +each repository are known and quite stable, the tool can hard-code them. +In addition, if a dependency is _art_-aware, the tool can take care of the +dependency subtree for that dependency recursively, by call the tool of that +dependency (duplicate metadata entries will be expected to be the same anyway). + + +### Results-level products + +Results-level products were introduced in _art_ as a mean to have data products +spanning multiple runs. While some interface is similar to the other principals +(`art::Event`, `art::Run`), their usage and accessibility is deeply different. + +The key point is this: **only output modules can access Results-level data +products** (not only `RootOutput` specifically). +This comes with a list of obvious and explicit clumsiness: there is no such a +thing like an _art_ module writing a Results-level data product. In fact, no +such a thing like an _art_ module _reading_ that data product either. +The `DumpJobEnvironment` facility offered here is in fact an _output module_: +producer and analysis modules can't learn the versions that the version +data products in the input files hold. They can however use the same algorithm +as `SaveJobEnvironment` uses (that is another output plugin) to extract the +versions it then saved in the Results-type data product: the information +extraction algorithm is factorized as `sbn::JobEnvironmentInfoExtractor` and its +return type is a class `sbn::JobEnvironmentInfo` (`sbnobj`) well suited to be +a _art_ data product. + + + +How to arrange a repository with the tool +------------------------------------------ + +The tools will depend on the repository version libraries of the current package +and its dependencies, and on _art_. It is recommended to place their source code +in the same directory as the repository version library. +Step by step: + + 1. Utilize the `/Metadata` directory where the version library + source code should already be. In that case, the name of that library for + linking will be `::Metadata`. + 2. Create the tool source `RepositoryVersion_tool.cc` by + copying the source code from + `sbncode/Metadata/sbncodeRepositoryVersion_tool.cc` or + `icaruscode/Metadata/icaruscodeRepositoryVersion_tool.cc`. + 3. Add to the `CMakeLists.txt` file instructions to build the tool: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cmake} + cet_build_plugin(RepositoryVersion art::tool + LIBRARIES + ::Metadata + sbncode::Metadata + sbnobj::Common_Metadata + ) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Additional libraries may be needed for the dependencies. + 4. Customize the tool source. + * If this repository _has_ dependent non-art repositories, add them to the + tool (the one in `icaruscode`, `icaruscodeRepositoryVersion`, already + shows how, with `icarusalg`): link to the dependent version libraries, + and add metadata entries for them. + * If this repository _is_ a dependency of another _art_-aware repository, + considering updating the tool in that repository to load this one and + store information from it. The tool in `icaruscode`, + `icaruscodeRepositoryVersion`, shows how (with `sbncode`). + 5. In the relevant configurations of the output plugin `SaveJobEnvironment`, + add the name of the repository the tool is covering (the `` + above). + + +How metadata can be accessed from the input files +================================================== + +The plugin `SaveJobEnvironment` can write the metadata into the _art_/ROOT file +and into the `TFileService` output file. + +The metadata in the _art_/ROOT file is stored as a Results-level data product, +and can therefore be read only by output modules. That does mean that producer +and analyzer _art_ modules will not be able to access that metadata. +However, an output module `DumpJobEnvironment` is provided that dumps the full +metadata on console output (configurable via `message` service). +The configuration of that module is as simple as: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +outputs.metadataDumper: { + module_type: "DumpJobEnvironment" +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +and, as usual, `metadataDumper` must be added in one of the end paths in the +`physics` configuration table. This type of configuration is provided in +`sbncode` as `dump_jobmetadata.fcl`. + +Metadata saved into a `TFileService` ROOT file will be in the main ROOT +directory of the file. See the documentation of `SaveJobEnvironment` plugin +for the details. + + + +Future improvements +==================== + +* Currently the metadata seen from all input so far is stored into the current + output file; it may happen then that an output file stores metadata from an + input file that was already closed when the output file started been filled. + That metadata has no reason to reside in that output file. +* There is going to be a large amount of duplication: all input files likely + have almost identical metadata, but we save each individually. A system to + collect the shared metadata in a single list may be desirable to reduce the + metadata size. +* If there is demand, the system can be extended to create also a FHiCL + configuration with the version of the repository. + + + */ diff --git a/sbncode/Metadata/SaveJobEnvironment_plugin.cc b/sbncode/Metadata/SaveJobEnvironment_plugin.cc new file mode 100644 index 000000000..70364dc4d --- /dev/null +++ b/sbncode/Metadata/SaveJobEnvironment_plugin.cc @@ -0,0 +1,331 @@ +/** + * @file sbncode/Metadata/SaveJobEnvironment_plugin.cc + * @brief Producer module writing job environment information into output. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 16, 2025 + */ + +// SBN libraries +#include "sbncode/Metadata/JobEnvironmentInfoExtractor.h" +#include "sbnobj/Common/Metadata/JobEnvironmentInfo.h" + +// framework libraries +#include "art_root_io/TFileService.h" +#include "art_root_io/TFileDirectory.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Core/ResultsProducer.h" +#include "art/Framework/Principal/Results.h" +#include "art/Framework/Principal/Handle.h" +#include "canvas/Utilities/Exception.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "fhiclcpp/types/Sequence.h" +#include "fhiclcpp/types/OptionalAtom.h" + +// ROOT libraries +#include "TFile.h" +#include "TDirectory.h" + +// C/C++ libraries +#include // std::back_inserter() +#include // std::make_unique() +#include +#include // std::move() +#include + + + +// ----------------------------------------------------------------------------- +namespace sbn { class SaveJobEnvironment; } +/** + * @brief Writes information from the job execution environment into output. + * + * This result-level module writes into the `art::Results` an object with + * information about the environment where the job is being executed. + * + * Information is extracted by the `sbn::JobEnvironmentInfoExtractor` algorithm. + * + * To add a new repository to the source of metadata collected by this plugin, + * see the instructions and explanations in + * @ref SBNsourceMetadataSystem "the SBN metadata system documentation". + * + * + * Output + * ------- + * + * In _art_/ROOT output file, `art::Results` level: + * * `std::vector` objects including all the ones + * found in the input files, and in addition, for this job: + * * a snapshot of the full environment (like in `getEnv()` C function), + * lexicographically sorted as for the object requirement. + * + * The information from this job is also saved in the `TFileService` output + * file, if that service is configured. Input information is currently not + * written into the `TFileService` output. Also note that the information is + * saved in the main directory of the file, as opposed to the usual subdirectory + * named after the module. The name of the object is `EnvInfo`, and, if + * `sbn::JobEnvironmentInfo` dictionary library is available to ROOT, one simple + * way to see it is to open the ROOT file with the ROOT interpreter and execute + * `EvtInfo->dump(std::cout);`. + * + * + * Module configuration + * --------------------- + * + * * `Repositories` (list of strings; default: see `DefaultRepositories`): + * the list of repositories whose version reporting tool will be queried. + * Directly passed as `JobEnvironmentInfoExtractor::Config::repositories` + * (see) to the exctraction algorithm. + * * `WriteToTFileService` (flag, optional): if set to `false`, the information + * will not be written into the `TFileService` output file; if it is set to + * `true`, it will be written into the output of file the service, which is + * required to be configured; if the parameter is omitted, the information + * will be written only if the service is available. + * + * + * Service dependencies + * --------------------- + * + * * `TFileService` (required only if `WriteToTFileService` is set to `true`) + * for output into the `TFile` managed by that service. + * + */ +class sbn::SaveJobEnvironment: public art::ResultsProducer { + + public: + + /// List of repository tools to be loaded by default. + static std::vector const DefaultRepositories; + + struct Config { + + using Name = fhicl::Name; + using Comment = fhicl::Comment; + + fhicl::Sequence Repositories{ + Name{ "Repositories" }, + Comment{ "Names of the repositories with version reporting tools" }, + DefaultRepositories + }; + + fhicl::OptionalAtom WriteToTFileService{ + Name{ "WriteToTFileService" }, + Comment + { "enables or disables writing to TFileService (default: if available)" } + }; + + }; // Config + + using Parameters = Table; + + + /// Constructor. + SaveJobEnvironment(Parameters const& params); + + /// Fetches the information. + virtual void beginJob() override; + + /// Reads the information on each new input file. + virtual void readResults(art::Results const& results) override; + + /// Writes the information at output closure time. + virtual void writeResults(art::Results& results) override; + + /// Clears after writing: does nothing. + virtual void clear() override; + + /// Write information to `TFileService` file if available. + virtual void endJob() override; + + + private: + + // --- BEGIN --- Configuration ----------------------------------------------- + + /// Names of the repositories to query for version metadata. + std::vector const fRepositoryNames; + + bool const fWriteToTFileService; ///< Whether to write info to `TFileService`. + + // --- END ----- Configuration ----------------------------------------------- + + + sbn::JobEnvironmentInfoExtractor fInfoExtractor; ///< Extraction algorithm. + + ///< All information from the input to be written, in the order it was read. + std::vector fInputInfo; + + /// Information from this job to be written. + sbn::JobEnvironmentInfo fJobInfo; + + + /// Fetches and returns all the information. + sbn::JobEnvironmentInfo fetchInformation(); + + + /// Writes the current information into `outDir`. + void writeInformationToTFile(TDirectory& outDir) const; + + /// Returns a correctly configured information extractor algorithm. + sbn::JobEnvironmentInfoExtractor makeInfoExtractor() const; + + + /// Returns whether we should write to `TFileService`. + static bool parseWriteToTFileService + (std::optional writeToTFileService); + + +}; // sbn::SaveJobEnvironment + + +// ----------------------------------------------------------------------------- +// --- Implementation +// ----------------------------------------------------------------------------- +std::vector const sbn::SaveJobEnvironment::DefaultRepositories { + "sbncode", "sbndcode", "icaruscode" +}; + + +// ----------------------------------------------------------------------------- +sbn::SaveJobEnvironment::SaveJobEnvironment(Parameters const& params) + : fRepositoryNames{ params().Repositories() } + , fWriteToTFileService + { parseWriteToTFileService(params().WriteToTFileService()) } + , fInfoExtractor{ makeInfoExtractor() } +{ + + produces>(); + + if (fWriteToTFileService) { + mf::LogInfo{ "SaveJobEnvironment" } + << "Will also save information into TFileService output file."; + } + +} // sbn::SaveJobEnvironment::SaveJobEnvironment() + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::beginJob() { + + fJobInfo = fetchInformation(); + +} + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::readResults(art::Results const& results) { + + std::vector const infoHandles + = results.getMany>(); + + mf::LogDebug{ "SaveJobEnvironment" } + << "Found " << infoHandles.size() << " job information products in input."; + + for (art::Handle> const& infoHandle + : infoHandles) + { + if (!infoHandle.isValid()) continue; + std::copy + (infoHandle->begin(), infoHandle->end(), back_inserter(fInputInfo)); + } // for + +} // sbn::SaveJobEnvironment::readResults() + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::writeResults(art::Results& results) { + + mf::LogDebug{ "SaveJobEnvironment" } + << "Information saved into art/ROOT output file:\n\n" << fJobInfo; + + auto allInfo + = std::make_unique>(fInputInfo); + allInfo->push_back(fJobInfo); + + // one copy each call + results.put(std::move(allInfo)); + +} + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::clear() { +} + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::endJob() { + + if (fWriteToTFileService) { + + writeInformationToTFile(art::ServiceHandle()->file()); + + } + +} // sbn::SaveJobEnvironment::endJob() + + +// ----------------------------------------------------------------------------- +sbn::JobEnvironmentInfo sbn::SaveJobEnvironment::fetchInformation() { + + return fInfoExtractor.extract(moduleDescription()); + +} + + +// ----------------------------------------------------------------------------- +void sbn::SaveJobEnvironment::writeInformationToTFile(TDirectory& outDir) const +{ + + // assuming copy constructor will be used + outDir.WriteObject(&fJobInfo, "EnvInfo"); + +} // sbn::SaveJobEnvironment::writeInformationToTFile() + + +// ----------------------------------------------------------------------------- +bool sbn::SaveJobEnvironment::parseWriteToTFileService + (std::optional writeToTFileService) +{ + // if is requested that we don't write it to TFileService, stop it here: + if (!writeToTFileService.value_or(true)) return false; + + // so either there is no explicit request, or that request is to use it: + // at this point, we do need to know if TFileService is available + // and if it's there, we'll want to write into it + try { + art::ServiceHandle(); + return true; + } + catch(art::Exception& e) { + if (e.categoryCode() != art::errors::ServiceNotFound) + throw; // something else entirely is happening: propagate the exception + + // not available: do we need it? + if (!writeToTFileService) return false; // no explicit request, so no + + // there is a request (and if we are here it was positive): complain! + throw art::Exception{ art::errors::Configuration, "", e } + << "SaveJobEnvironment explicitly requested saving into TFileService" + " output, but TFileService is not configured.\n"; + + } + +} // sbn::SaveJobEnvironment::parseWriteToTFileService() + + +// ----------------------------------------------------------------------------- +sbn::JobEnvironmentInfoExtractor +sbn::SaveJobEnvironment::makeInfoExtractor() const { + + JobEnvironmentInfoExtractor::Config config; + config.repositories = fRepositoryNames; + return JobEnvironmentInfoExtractor{ config }; + +} // sbn::SaveJobEnvironment::makeInfoExtractor() + + +// ----------------------------------------------------------------------------- +DEFINE_ART_RESULTS_PLUGIN(sbn::SaveJobEnvironment) + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/Metadata/sbncodeRepositoryVersion_tool.cc b/sbncode/Metadata/sbncodeRepositoryVersion_tool.cc new file mode 100644 index 000000000..8941ac4e2 --- /dev/null +++ b/sbncode/Metadata/sbncodeRepositoryVersion_tool.cc @@ -0,0 +1,62 @@ +/** + * @file RepositoryVersion_sbncode_tool.cc + * @brief _art_ tool reporting the version of `sbncode`-related packages. + * @author Gianluca Petrillo (petrillo@slac.stanford.edu) + * @date January 18, 2025 + * + */ + +// SBN libraries +#include "sbncode/Metadata/RepositoryVersionReporter.h" +#include "sbncode/Metadata/RepositoryVersion_sbncode.h" +#include "sbnobj/Metadata/RepositoryVersion_sbnobj.h" + +// framework libraries +#include "art/Utilities/ToolMacros.h" +#include "art/Utilities/ToolConfigTable.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +// C++ standard libraries +#include +#include + + +// ----------------------------------------------------------------------------- +namespace sbn { struct sbncodeRepositoryVersion; } +/** + * @brief Implements the `sbn::RepositoryVersionReporter` interface for + * `sbncode`. + * + * It collects information from the following repositories: `sbncode` and + * `sbnobj`. + * + */ +struct sbn::sbncodeRepositoryVersion: public sbn::RepositoryVersionReporter { + + struct Config {}; + + using Parameters = art::ToolConfigTable; + + sbncodeRepositoryVersion(Parameters const&); + +}; // sbn::sbncodeRepositoryVersion() + + +// ----------------------------------------------------------------------------- +// --- implementation +// ----------------------------------------------------------------------------- +sbn::sbncodeRepositoryVersion::sbncodeRepositoryVersion(Parameters const&) { + + packageVersions.items.emplace_back("sbnobj", ::RepositoryVersion_sbnobj); + packageVersions.items.emplace_back("sbncode", ::RepositoryVersion_sbncode); + + packageVersions.finish(); + +} + + +// ----------------------------------------------------------------------------- +DEFINE_ART_CLASS_TOOL(sbn::sbncodeRepositoryVersion) + + +// ----------------------------------------------------------------------------- diff --git a/sbncode/NuMuSelection/CMakeLists.txt b/sbncode/NuMuSelection/CMakeLists.txt index c188909b9..485fa77a0 100644 --- a/sbncode/NuMuSelection/CMakeLists.txt +++ b/sbncode/NuMuSelection/CMakeLists.txt @@ -1,5 +1,5 @@ cet_build_plugin( MuPVertexStudy art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -25,7 +25,7 @@ cet_build_plugin( MuPVertexStudy art::module larreco::RecoAlg ) cet_build_plugin( MuonS2NStudy art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -51,7 +51,7 @@ cet_build_plugin( MuonS2NStudy art::module larreco::RecoAlg ) cet_build_plugin( NuMuEfficiencyStudy art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service diff --git a/sbncode/OpDet/CMakeLists.txt b/sbncode/OpDet/CMakeLists.txt index 8eb4378a4..445995f0e 100644 --- a/sbncode/OpDet/CMakeLists.txt +++ b/sbncode/OpDet/CMakeLists.txt @@ -1,6 +1,12 @@ cet_enable_asserts() -cet_build_plugin(PDMapAlgSimple art::tool LIBRARIES +cet_make_library( + LIBRARY_NAME OpDet_PDMapAlg INTERFACE + SOURCE + PDMapAlg.h +) + +cet_build_plugin(PDMapAlgSimple art::tool LIBRARIES PRIVATE art::Utilities ) diff --git a/sbncode/OpDet/PDMapAlg.h b/sbncode/OpDet/PDMapAlg.h index e0b79fae1..d9a9f687e 100644 --- a/sbncode/OpDet/PDMapAlg.h +++ b/sbncode/OpDet/PDMapAlg.h @@ -12,7 +12,7 @@ #ifndef SBN_OpDet_PDMapAlg_H #define SBN_OpDet_PDMapAlg_H -#include "fhiclcpp/ParameterSet.h" +//#include "fhiclcpp/ParameterSet.h" //#include "art/Utilities/ToolMacros.h" #include diff --git a/sbncode/OpT0Finder/flashmatch/Algorithms/CMakeLists.txt b/sbncode/OpT0Finder/flashmatch/Algorithms/CMakeLists.txt index c6ee1e2d3..eb08314d2 100644 --- a/sbncode/OpT0Finder/flashmatch/Algorithms/CMakeLists.txt +++ b/sbncode/OpT0Finder/flashmatch/Algorithms/CMakeLists.txt @@ -2,7 +2,7 @@ art_make_library( LIBRARIES larsim::PhotonPropagation larsim::PhotonPropagation_PhotonVisibilityService_service - larsim::LegacyLArG4 + #larsim::LegacyLArG4 #sbncode_OpT0Finder_flashmatch_GeoAlgo sbncode_OpT0Finder_flashmatch_Base larcorealg::GeoAlgo diff --git a/sbncode/PID/CMakeLists.txt b/sbncode/PID/CMakeLists.txt index 1d5da7ad7..b63ec1cf4 100644 --- a/sbncode/PID/CMakeLists.txt +++ b/sbncode/PID/CMakeLists.txt @@ -1,6 +1,6 @@ cet_build_plugin( Dazzle art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -27,7 +27,7 @@ cet_build_plugin( Dazzle art::module ) cet_build_plugin( Razzle art::module - LIBRARIES + LIBRARIES PRIVATE art::Framework_Core art::Framework_Services_Registry art_root_io::TFileService_service @@ -54,7 +54,7 @@ cet_build_plugin( Razzle art::module ) cet_build_plugin( Razzled art::module - LIBRARIES + LIBRARIES PRIVATE ROOT::TMVA canvas::canvas art_root_io::TFileService_service diff --git a/sbncode/SBNEventWeight/App/CMakeLists.txt b/sbncode/SBNEventWeight/App/CMakeLists.txt index 2989659b7..6bb912864 100644 --- a/sbncode/SBNEventWeight/App/CMakeLists.txt +++ b/sbncode/SBNEventWeight/App/CMakeLists.txt @@ -1,12 +1,15 @@ cet_build_plugin(SBNEventWeight art::module - LIBRARIES - sbnobj::Common_SBNEventWeight sbncode_SBNEventWeight_Base + LIBRARIES PRIVATE + sbnobj::Common_SBNEventWeight + sbncode_SBNEventWeight_Base sbncode_SBNEventWeight_Calculators_CrossSection - sbncode_SBNEventWeight_Calculators_BNBFlux nusimdata::SimulationBase + sbncode_SBNEventWeight_Calculators_BNBFlux + sbncode_SBNEventWeight_Calculators_Geant4 + nusimdata::SimulationBase ROOT::Geom) cet_build_plugin(SystToolsEventWeight art::module - LIBRARIES + LIBRARIES PRIVATE sbnobj::Common_SBNEventWeight sbncode_SBNEventWeight_Base systematicstools::interface systematicstools::interpreters diff --git a/sbncode/SBNEventWeight/CMakeLists.txt b/sbncode/SBNEventWeight/CMakeLists.txt index de59387f6..9f4018c28 100644 --- a/sbncode/SBNEventWeight/CMakeLists.txt +++ b/sbncode/SBNEventWeight/CMakeLists.txt @@ -1,3 +1,6 @@ +include_directories ( $ENV{GEANT4REWEIGHT_INC} ) +link_directories ( $ENV{GEANT4REWEIGHT_LIB} ) + add_subdirectory(Base) add_subdirectory(Calculators) add_subdirectory(App) diff --git a/sbncode/SBNEventWeight/Calculators/BNBFlux/FluxCalcPrep.cxx b/sbncode/SBNEventWeight/Calculators/BNBFlux/FluxCalcPrep.cxx index 640a84769..ff2b10b99 100644 --- a/sbncode/SBNEventWeight/Calculators/BNBFlux/FluxCalcPrep.cxx +++ b/sbncode/SBNEventWeight/Calculators/BNBFlux/FluxCalcPrep.cxx @@ -213,10 +213,18 @@ namespace sbn { // std::cout<<"SBNEventWeight : getweight for the "< > mcFluxHandle; - e.getByLabel(fGeneratorModuleLabel,mcFluxHandle); + e.getByLabel(fGeneratorModuleLabel, mcFluxHandle); std::vector const& fluxlist = *mcFluxHandle; + + art::Handle< std::vector > dk2nuHandle; + std::vector const* dk2nu_v = nullptr; + e.getByLabel(fGeneratorModuleLabel, dk2nuHandle); + if (dk2nuHandle.isValid()) { + dk2nu_v = dk2nuHandle.product(); + } + //or do the above 3 lines in one line -// auto const& mclist = *e.getValidHandle>(fGeneratorModuleLabel); + auto const& mclist = *e.getValidHandle>(fGeneratorModuleLabel); // If no neutrinos in this event, gives 0 weight; int NUni = fParameterSet.fNuniverses; @@ -256,12 +264,12 @@ namespace sbn { } // Collect neutrino energy; mclist is replaced with fluxlist. -// double enu= mclist[inu].GetNeutrino().Nu().E(); double enu= fluxlist[inu].fnenergyn; + if ( enu == -1 ) enu= mclist[inu].GetNeutrino().Nu().E(); //For MeVPrtl generator, not all MCFlux result in a neutrino, so equivalent neutrino energy is computed and saved as MCTruth //Let's make a weights based on the calculator you have requested - if(fParameterSet.fRWType == EventWeightParameterSet::kMultisim){ + if(fParameterSet.fRWType == EventWeightParameterSet::kMultiSim){ for (size_t i=0;isecond[i]; @@ -287,13 +295,34 @@ namespace sbn { // First let's check that the parent of the neutrino we are looking for is // the particle we intended it to be, if not set all weights to 1 - if (std::find(fprimaryHad.begin(), fprimaryHad.end(),(fluxlist[inu].ftptype)) == fprimaryHad.end() ){//if it does not contain any particles we need get 1 + + simb::MCFlux flux; + flux.ftptype = fluxlist[inu].ftptype; + flux.ftpx = fluxlist[inu].ftpx; + flux.ftpy = fluxlist[inu].ftpy; + flux.ftpz = fluxlist[inu].ftpz; + + // If Dk2Nu flux, use ancestors to evaluate tptype + if (fluxlist[inu].fFluxType == simb::kDk2Nu) { + + for( const bsim::Ancestor & ancestor : dk2nu_v->at(inu).ancestor ) { + std::string aproc = ancestor.proc; + if( (aproc.find("BooNEHadronInelastic:BooNEpBeInteraction") != std::string::npos) && (aproc.find("QEBooNE") == std::string::npos) ) { + flux.ftptype = ancestor.pdg; + flux.ftpx = ancestor.startpx; + flux.ftpy = ancestor.startpy; + flux.ftpz = ancestor.startpz; + } // found it + } // find first inelastic + } + + if (std::find(fprimaryHad.begin(), fprimaryHad.end(),(flux.ftptype)) == fprimaryHad.end() ){//if it does not contain any particles we need get 1 weights.resize( NUni); std::fill(weights.begin(), weights.end(), 1); return weights;//done, all 1 }// Hadronic parent check - if(fParameterSet.fRWType == EventWeightParameterSet::kMultisim){ + if(fParameterSet.fRWType == EventWeightParameterSet::kMultiSim){ for (unsigned int i = 0; int(weights.size()) < NUni; i++) {//if all weights are 1, no need to calculate weights; std::pair test_weight; @@ -301,19 +330,18 @@ namespace sbn { std::vector< float > Vrandom = (fParameterSet.fParameterMap.begin())->second;//vector of random # std::vector< float > subVrandom;//sub-vector of random numbers; if( CalcType == "PrimaryHadronNormalization"){//Normalization - test_weight = PHNWeightCalc(fluxlist[inu], Vrandom[i]); + test_weight = PHNWeightCalc(flux, Vrandom[i]); } else { subVrandom = {Vrandom.begin()+i*FitCov->GetNcols(), Vrandom.begin()+(i+1)*FitCov->GetNcols()}; if( CalcType == "PrimaryHadronFeynmanScaling"){//FeynmanScaling - test_weight = PHFSWeightCalc(fluxlist[inu], subVrandom); + test_weight = PHFSWeightCalc(flux, subVrandom); } else if( CalcType == "PrimaryHadronSanfordWang"){//SanfordWang - test_weight = PHSWWeightCalc(fluxlist[inu], subVrandom); + test_weight = PHSWWeightCalc(flux, subVrandom); } else if( CalcType == "PrimaryHadronSWCentralSplineVariation"){//SWCentaralSplineVariation - test_weight = PHSWCSVWeightCalc(fluxlist[inu], subVrandom); - + test_weight = PHSWCSVWeightCalc(flux, subVrandom); } else throw cet::exception(__PRETTY_FUNCTION__) << GetName() << ": this shouldnt happen.."< > ThinSliceBetheBloch(G4ReweightTraj * theTraj, double res, double mass, bool isElastic){ + + std::vector< std::pair > result; + + //First slice position +// double sliceEdge = res; +// double lastPos = 0.; +// double nextPos = 0.; +// double px,py,pz; + int interactInSlice = 0; + + //Get total distance traveled in z +// double totalDeltaZ = 0.; + double disp = 0.; +// double oldDisp = 0.; +// int crossedSlices = 0; + + int currentSlice = 0; + int oldSlice = 0; + + double sliceEnergy = theTraj->GetEnergy(); + size_t nSteps = theTraj->GetNSteps(); + for(size_t is = 0; is < nSteps; ++is){ + auto theStep = theTraj->GetStep(is); + + disp += theStep->GetStepLength(); + currentSlice = floor(disp/res); + + std::string theProc = theStep->GetStepChosenProc(); + + //Check to see if in a new slice and it's not the end + if( oldSlice != currentSlice && is < nSteps - 1){ + + + //Save Interaction info of the prev slice + //and reset + result.push_back( std::make_pair(sliceEnergy, interactInSlice) ); + interactInSlice = 0; + + //Update the energy + sliceEnergy = sliceEnergy - res*BetheBloch(sliceEnergy, mass); + if( sliceEnergy - mass < 0.){ + //std::cout << "Warning! Negative energy " << sliceEnergy - mass << std::endl; + //std::cout << "Crossed " << oldSlice - currentSlice << std::endl; + sliceEnergy = 0.0001; + } + //If it's more than 1 slice, add in non-interacting slices + for(int ic = 1; ic < abs( oldSlice - currentSlice ); ++ic){ + //std::cout << ic << std::endl; + + result.push_back( std::make_pair(sliceEnergy, 0) ); + + //Update the energy again + sliceEnergy = sliceEnergy - res*BetheBloch(sliceEnergy, mass); + if( sliceEnergy - mass < 0.){ + //std::cout << "Warning! Negative energy " << sliceEnergy - mass << std::endl; + //std::cout << "Crossed " << oldSlice - currentSlice << std::endl; + sliceEnergy = 0.0001; + } + } + + if ((!isElastic && theProc.find(std::string("Inelastic")) != std::string::npos) || (isElastic && theProc.find(std::string("hadElastic")) != std::string::npos)) { + // std::cout << "found! " << theProc << '\n'; + interactInSlice = 1; + } + } + //It's crossed a slice and it's the last step. Save both info + else if( oldSlice != currentSlice && is == nSteps - 1 ){ + result.push_back( std::make_pair(sliceEnergy, interactInSlice) ); + interactInSlice = 0; + + //Update the energy + sliceEnergy = sliceEnergy - res*BetheBloch(sliceEnergy, mass); + if( sliceEnergy - mass < 0.){ + //std::cout << "Warning! Negative energy " << sliceEnergy - mass << std::endl; + //std::cout << "Crossed " << oldSlice - currentSlice << std::endl; + sliceEnergy = 0.0001; + } + //If it's more than 1 slice, add in non-interacting slices + for(int ic = 1; ic < abs( oldSlice - currentSlice ); ++ic){ + //std::cout << ic << std::endl; + + result.push_back( std::make_pair(sliceEnergy, 0) ); + + //Update the energy again + sliceEnergy = sliceEnergy - res*BetheBloch(sliceEnergy, mass); + if( sliceEnergy - mass < 0.){ + //std::cout << "Warning! Negative energy " << sliceEnergy - mass << std::endl; + //std::cout << "Crossed " << oldSlice - currentSlice << std::endl; + sliceEnergy = 0.0001; + } + } + + //Save the last slice + if ((!isElastic && theProc.find(std::string("Inelastic")) != std::string::npos) || (isElastic && theProc.find(std::string("hadElastic")) != std::string::npos)) { + // std::cout << "found! " << theProc << '\n'; + interactInSlice = 1; + } + result.push_back( std::make_pair(sliceEnergy, interactInSlice) ); + } + //It's the end, so just save this last info + else if( oldSlice == currentSlice && is == nSteps - 1 ){ + if ((!isElastic && theProc.find(std::string("Inelastic")) != std::string::npos) || (isElastic && theProc.find(std::string("hadElastic")) != std::string::npos)) { + // std::cout << "found! " << theProc << '\n'; + interactInSlice = 1; + } + result.push_back( std::make_pair(sliceEnergy, interactInSlice) ); + } + //Same slice, not the end. Check for interactions + else{ + if ((!isElastic && theProc.find(std::string("Inelastic")) != std::string::npos) || (isElastic && theProc.find(std::string("hadElastic")) != std::string::npos)) { + // std::cout << "found! " << theProc << '\n'; + interactInSlice = 1; + } + } + + //Update oldslice + oldSlice = currentSlice; + } + + return result; +} diff --git a/sbncode/SBNEventWeight/Calculators/Geant4/CMakeLists.txt b/sbncode/SBNEventWeight/Calculators/Geant4/CMakeLists.txt new file mode 100644 index 000000000..b064daff0 --- /dev/null +++ b/sbncode/SBNEventWeight/Calculators/Geant4/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories ( $ENV{GEANT4_FQ_DIR}/include ) +include_directories ( $ENV{GEANT4REWEIGHT_INC} ) +link_directories ( $ENV{GEANT4REWEIGHT_LIB} ) + +art_make_library( + LIBRARY_NAME sbncode_SBNEventWeight_Calculators_Geant4 + LIBRARIES + sbncode_SBNEventWeight_Base + nusimdata::SimulationBase + nurandom::RandomUtils_NuRandomService_service + art::Framework_Principal + art::Framework_Services_Registry + art_root_io::TFileService_service + larcore::Geometry_Geometry_service + cetlib_except::cetlib_except + Geant4::G4ptl + ReweightBaseLib + PropBaseLib + ROOT::Tree +) + +install_headers() +install_fhicl() +install_source() + diff --git a/sbncode/SBNEventWeight/Calculators/Geant4/Geant4WeightCalc.cxx b/sbncode/SBNEventWeight/Calculators/Geant4/Geant4WeightCalc.cxx new file mode 100644 index 000000000..86e84a76d --- /dev/null +++ b/sbncode/SBNEventWeight/Calculators/Geant4/Geant4WeightCalc.cxx @@ -0,0 +1,472 @@ +/** + * \class evwgh::Geant4WeightCalc + * \brief Updated hadron reinteraction event reweighting, using geant4reweight + * \author K. Duffy , 2019/10 + * + * Reweight events based on hadron reinteraction probabilities. + */ + +#include +#include +#include "TDirectory.h" +#include "TFile.h" +#include "TH1D.h" +#include "TTree.h" +#include "Geant4/G4LossTableManager.hh" +#include "Geant4/G4ParticleTable.hh" +#include "Geant4/G4ParticleDefinition.hh" +#include "Geant4/G4Material.hh" +#include "Geant4/G4MaterialCutsCouple.hh" +#include "art_root_io/TFileService.h" +#include "art_root_io/TFileDirectory.h" +#include "art/Framework/Services/Registry/ServiceHandle.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Persistency/Provenance/ModuleContext.h" +#include "art/Framework/Principal/Event.h" +#include "canvas/Persistency/Common/FindManyP.h" +#include "canvas/Persistency/Common/Ptr.h" +#include "nusimdata/SimulationBase/MCParticle.h" +#include "nusimdata/SimulationBase/MCTruth.h" +#include "CLHEP/Random/RandGaussQ.h" +#include "CLHEP/Units/PhysicalConstants.h" +#include "CLHEP/Units/SystemOfUnits.h" +#include "sbncode/SBNEventWeight/Base/WeightCalcCreator.h" +#include "sbncode/SBNEventWeight/Base/WeightCalc.h" +#include "larcore/Geometry/Geometry.h" +#include "geant4reweight/ReweightBase/G4ReweightManager.hh" +#include "geant4reweight/ReweightBase/G4ReweighterFactory.hh" +#include "geant4reweight/ReweightBase/G4Reweighter.hh" +#include "geant4reweight/ReweightBase/G4ReweightTraj.hh" +#include "geant4reweight/ReweightBase/G4ReweightStep.hh" +#include "geant4reweight/PropBase/G4ReweightParameterMaker.hh" + +// local include +#include "BetheBlochForG4ReweightValid.h" + +namespace sbn { + +namespace evwgh { + +class Geant4WeightCalc : public WeightCalc { +public: + Geant4WeightCalc() {} + + void Configure(fhicl::ParameterSet const& p, CLHEP::HepRandomEngine& engine); + + std::vector GetWeight(art::Event& e, size_t inu); + +private: + std::string fMCParticleProducer; //!< Label for the MCParticle producer + std::string fMCTruthProducer; //!< Label for the MCTruth producer + CLHEP::RandGaussQ* fGaussRandom; //!< Random number generator + fhicl::ParameterSet fMaterial; //!< Detector material, i.e. LAr + unsigned fNsims; //!< Number of universes (multisim mode) + unsigned fNsigmas; //!< Number of sigmas (multisigma mode) + int fPdg; //!< PDG value for particles that a given weight calculator should apply to. Note that for now this module can only handle weights for one particle species at a time. + G4ReweighterFactory fRWFactory; //!< Base class to handle all Geant4Reweighters + G4Reweighter *fReweighter; //!< Geant4Reweighter -- this is what provides the weights + G4ReweightParameterMaker *fParMaker; + std::vector> UniverseVals; //!< Vector of maps relating parameter name to value (defines parameter values that will be evaluated in universes). Each map should have one entry per parameter we are considering + + art::ServiceHandle < geo::Geometry > fGeometryService; + + bool fMakeOutputTrees; ///!< Fcl parameter to decide whether to save output tree (useful for validations but not necessary when running in production) + TTree *fOutTree_MCTruth; //!< Output tree for quick validations: on entry per MCTruth object + TTree *fOutTree_Particle; //!< Output tree for quick validations: one entry per neutrino-induced pi+, pi-, or proton + int event_num; //!< Variables for both output trees + int run_num; //!< Variables for both output trees + int subrun_num; //!< Variables for both output trees + double p_track_length; //!< Variables for by-particle output tree + int p_PDG; //!< Variables for by-particle output tree + std::string p_final_proc; //!< Variables for by-particle output tree + double p_init_momentum; //!< Variables for by-particle output tree + double p_final_momentum; //!< Variables for by-particle output tree + std::vector< double > p_energies_el; //!< Variables for by-particle output tree + std::vector< int > p_sliceInts_el; //!< Variables for by-particle output tree + std::vector< double > p_energies_inel; //!< Variables for by-particle output tree + std::vector< int > p_sliceInts_inel; //!< Variables for by-particle output tree + int p_nElasticScatters; //!< Variables for by-particle output tree + std::vector p_weight; //!< Variables for by-particle output tree + + // **IF** I understand correctly, then after commit 11077f2 (el_weight*inel_weight) is returned by GetWeight() + + bool fDebug; //!< print debug statements + + DECLARE_WEIGHTCALC(Geant4WeightCalc) +}; + + +void Geant4WeightCalc::Configure(fhicl::ParameterSet const& p, + CLHEP::HepRandomEngine& engine) +{ + std::cout << "Using Geant4WeightCalc for reinteraction weights" << std::endl; + + // Get configuration for this function + fhicl::ParameterSet const& pset = p.get(GetName()); + fMCParticleProducer = pset.get("MCParticleProducer", "largeant"); + fMCTruthProducer = pset.get("MCTruthProducer", "generator"); + fMakeOutputTrees = pset.get< bool >( "makeoutputtree", false ); + std::string mode = pset.get("mode"); + std::string FracsFileName = pset.get< std::string >( "fracsfile" ); + std::vector< fhicl::ParameterSet > FitParSets = pset.get< std::vector< fhicl::ParameterSet > >("parameters"); + fNsims = pset.get ("number_of_multisims", 0); + fNsigmas = pset.get ("number_of_sigmas", 0); + fPdg = pset.get ("pdg_to_reweight"); + fMaterial = pset.get ("material"); + fDebug = pset.get ("debug",false); + + // Prepare random generator + fGaussRandom = new CLHEP::RandGaussQ(engine); + + // Get input file + TFile FracsFile( FracsFileName.c_str(), "OPEN" ); + + // Configure G4Reweighter + fParMaker = new G4ReweightParameterMaker( FitParSets, true , fPdg ); //TODO:Do we want check_overlap? Maybe a fcl switch? + static G4ReweightManager* RWManager = new G4ReweightManager( {fMaterial} ); //Constructor asks for a vector, but SBN only cares about one material + fReweighter = fRWFactory.BuildReweighter(fPdg, &FracsFile, fParMaker->GetFSHists(), fMaterial, RWManager, fParMaker->GetElasticHist() ); + + // Make output trees to save things for quick and easy validation + art::ServiceHandle tfs; + + if (fMakeOutputTrees){ + fOutTree_Particle = tfs->make(Form("%s_%i","ByParticleValidTree",fPdg),""); + fOutTree_Particle->Branch("event_num",&event_num); + fOutTree_Particle->Branch("run_num",&run_num); + fOutTree_Particle->Branch("subrun_num",&subrun_num); + fOutTree_Particle->Branch("UniverseVals", &UniverseVals); + fOutTree_Particle->Branch("pdg_to_reweight",&fPdg); + + fOutTree_Particle->Branch("weight",&p_weight); + fOutTree_Particle->Branch("track_length",&p_track_length); + fOutTree_Particle->Branch("PDG",&p_PDG); + fOutTree_Particle->Branch("final_proc",&p_final_proc); + fOutTree_Particle->Branch("init_momentum",&p_init_momentum); + fOutTree_Particle->Branch("final_momentum",&p_final_momentum); + fOutTree_Particle->Branch("energies_el",&p_energies_el); + fOutTree_Particle->Branch("sliceInts_el",&p_sliceInts_el); + fOutTree_Particle->Branch("energies_inel",&p_energies_inel); + fOutTree_Particle->Branch("sliceInts_inel",&p_sliceInts_inel); + fOutTree_Particle->Branch("nElasticScatters",&p_nElasticScatters); + + fOutTree_MCTruth = tfs->make(Form("%s_%i","ByMCTruthValidTree",fPdg),""); + fOutTree_MCTruth->Branch("event_num",&event_num); + fOutTree_MCTruth->Branch("run_num",&run_num); + fOutTree_MCTruth->Branch("subrun_num",&subrun_num); + fOutTree_MCTruth->Branch("UniverseVals", &UniverseVals); + fOutTree_MCTruth->Branch("pdg_to_reweight",&fPdg); + } + + // Read input parameter sets and set up universes + size_t n_parsets = FitParSets.size(); + std::vector FitParNames; + std::vector FitParNominals; + std::vector FitParSigmas; + std::map theNominals; + + std::cout << "Configuring parameter: " << GetFullName() << " with mode: " << mode << std::endl; + fParameterSet.Configure(GetFullName(), mode, fNsims); + + for (size_t i_parset=0; i_parset("Name"); + double theNominal = theSet.get("Nominal",1.); + double theSigma = theSet.get("Sigma",0.); + + FitParNames.push_back(theName); + FitParNominals.push_back(theNominal); + FitParSigmas.push_back(theSigma); + + if (fDebug) std::cout << "Name: " << theName << ", Nominal: " << theNominal << ", Sigma: " << theSigma << std::endl; + + theNominals[theName] = theNominal; + + fParameterSet.AddParameter(theName, theSigma); + } + + if (mode=="multisim"){ + // multisim mode: parameter values sample within the given uncertainty for all parameters simultaneously + // Loop over universes j + for (unsigned j=0; j tmp_vals; + for (size_t i_parset=0; i_parsetfire(0.0,1.0); + tmp_vals[FitParNames.at(i_parset)] = FitParNominals.at(i_parset)+(FitParSigmas.at(i_parset)*r); + } // loop over parameters (i_parset) + // Now save this universe + UniverseVals.push_back(tmp_vals); + } // loop over Nsims (j) + } // multisim + + else if (mode=="multisigma"){ + // pm1sigma mode: 0 = +1sigma, 1 = -1sigma of a single parameter. All other parameters at nominal + for (size_t i_parset=0; i_parset tmp_vals(theNominals); + tmp_vals[FitParNames.at(i_parset)] = FitParNominals.at(i_parset) + i_sigma*FitParSigmas.at(i_parset); + UniverseVals.push_back(tmp_vals); + } // ened loop over sigmas + } // end loop over parsets (i) + } // multisigma + + else{ + // Anything else mode: Set parameters to user-defined nominal value + UniverseVals.push_back(theNominals); + } // any other mode + + fNsims = UniverseVals.size(); + if (fDebug) std::cout << "Running mode: " << mode <<". Nsims = " << fNsims << std::endl; +} + + +std::vector Geant4WeightCalc::GetWeight(art::Event& e, size_t itruth ) { + + // Get event/run/subrun numbers for output + run_num = e.run(); + subrun_num = e.subRun(); + event_num = e.id().event(); + + // Get MCParticles for each MCTruth in this event + art::Handle > truthHandle; + e.getByLabel(fMCTruthProducer, truthHandle); + const art::FindManyP truthParticles(truthHandle, e, fMCParticleProducer); + assert(truthParticles.isValid()); + + // Initialize the vector of event weights + std::vector weight; + + // Initialize weight vector for this MCTruth + weight.clear(); + weight.resize(fNsims, 1.0); + + // Loop over MCParticles in the event + auto const& mcparticles = truthParticles.at(itruth); + + for (size_t i=0; i trajpoint_X; + std::vector trajpoint_Y; + std::vector trajpoint_Z; + std::vector trajpoint_PX; + std::vector trajpoint_PY; + std::vector trajpoint_PZ; + std::vector elastic_indices; + + //Get the list of processes from the true trajectory + const std::vector< std::pair< size_t, unsigned char > > & processes = p.Trajectory().TrajectoryProcesses(); + std::map< size_t, std::string > process_map; + for( auto it = processes.begin(); it != processes.end(); ++it ){ + process_map[ it->first ] = p.Trajectory().KeyToProcess( it->second ); + } + + for( size_t i = 0; i < p.NumberTrajectoryPoints(); ++i ){ + double X = p.Position(i).X(); + double Y = p.Position(i).Y(); + double Z = p.Position(i).Z(); + geo::Point_t testpoint1 { X, Y, Z }; + const TGeoMaterial* testmaterial1 = fGeometryService->Material( testpoint1 ); + if( ! testmaterial1 ) { + break; // The trajectory point is outside the world, or the geometry service is unable to handle the point + } + //For now, just going to reweight the points within the LAr of the TPC + // TODO check if this is right + if ( !strcmp( testmaterial1->GetName(), "LAr" ) ){ + trajpoint_X.push_back( X ); + trajpoint_Y.push_back( Y ); + trajpoint_Z.push_back( Z ); + + trajpoint_PX.push_back( p.Px(i) ); + trajpoint_PY.push_back( p.Py(i) ); + trajpoint_PZ.push_back( p.Pz(i) ); + + auto itProc = process_map.find(i); + if( itProc != process_map.end() && itProc->second == "hadElastic" ){ + //Push back the index relative to the start of the reweightable steps + elastic_indices.push_back( trajpoint_X.size() - 1 ); + // if (fDebug) std::cout << "Elastic index: " << trajpoint_X.size() - 1 << std::endl; + } + + } + + } // end loop over trajectory points + + // Now find daughters of the MCP + std::vector daughter_PDGs; + std::vector daughter_IDs; + for( int i_mcp = 0; i_mcp < p.NumberDaughters(); i_mcp++ ){ + int daughterID = p.Daughter(i_mcp); + for (auto test_mcp : mcparticles){ + if (test_mcp->TrackId() == daughterID){ + int pid = test_mcp->PdgCode(); + daughter_PDGs.push_back(pid); + daughter_IDs.push_back( test_mcp->TrackId() ); + break; + } + } + } // end loop over daughters + + // --- Now that we have all the information about the track we need, here comes the reweighting part! --- // + + //Make a G4ReweightTraj -- This is the reweightable object + G4ReweightTraj theTraj(mcpID, p_PDG, 0, event_num, std::make_pair(0,0)); + + //Create its set of G4ReweightSteps and add them to the Traj (note: this needs to be done once per MCParticle but will be valid for all weight calculations) + std::vector< G4ReweightStep* > allSteps; + + size_t nSteps = trajpoint_PX.size(); + + if( nSteps < 2 ) continue; + + p_nElasticScatters = elastic_indices.size(); + for( size_t istep = 1; istep < nSteps; ++istep ){ + + std::string proc = "default"; + if( istep == trajpoint_PX.size() - 1 ) + proc = EndProcess; + else if( std::find( elastic_indices.begin(), elastic_indices.end(), istep ) != elastic_indices.end() ){ + proc = "hadElastic"; + } + + double deltaX = ( trajpoint_X.at(istep) - trajpoint_X.at(istep-1) ); + double deltaY = ( trajpoint_Y.at(istep) - trajpoint_Y.at(istep-1) ); + double deltaZ = ( trajpoint_Z.at(istep) - trajpoint_Z.at(istep-1) ); + + double len = sqrt( + std::pow( deltaX, 2 ) + + std::pow( deltaY, 2 ) + + std::pow( deltaZ, 2 ) + ); + + double preStepP[3] = { + trajpoint_PX.at(istep-1)*1.e3, + trajpoint_PY.at(istep-1)*1.e3, + trajpoint_PZ.at(istep-1)*1.e3 + }; + + double postStepP[3] = { + trajpoint_PX.at(istep)*1.e3, + trajpoint_PY.at(istep)*1.e3, + trajpoint_PZ.at(istep)*1.e3 + }; + + if( istep == 1 ){ + theTraj.SetEnergy( sqrt( preStepP[0]*preStepP[0] + preStepP[1]*preStepP[1] + preStepP[2]*preStepP[2] + mass*mass ) ); + } + + G4ReweightStep * theStep = new G4ReweightStep( mcpID, p_PDG, 0, event_num, preStepP, postStepP, len, proc ); + theStep->SetDeltaX( deltaX ); + theStep->SetDeltaY( deltaY ); + theStep->SetDeltaZ( deltaZ ); + + theTraj.AddStep( theStep ); + + } // end loop over nSteps (istep) + + // add daughters + for( size_t k = 0; k < daughter_PDGs.size(); ++k ){ + theTraj.AddChild( new G4ReweightTraj(daughter_IDs[k], daughter_PDGs[k], mcpID, event_num, std::make_pair(0,0) ) ); + } + + p_track_length = theTraj.GetTotalLength(); + + p_init_momentum = sqrt( theTraj.GetEnergy()*theTraj.GetEnergy() - mass*mass ); + p_final_momentum = sqrt( + std::pow( theTraj.GetStep( theTraj.GetNSteps() - 1 )->GetPreStepPx(), 2 ) + + std::pow( theTraj.GetStep( theTraj.GetNSteps() - 1 )->GetPreStepPy(), 2 ) + + std::pow( theTraj.GetStep( theTraj.GetNSteps() - 1 )->GetPreStepPz(), 2 ) + ); + + std::vector< std::pair< double, int > > thin_slice_inelastic = ThinSliceBetheBloch( &theTraj, .5, mass , false); + std::vector< std::pair< double, int > > thin_slice_elastic = ThinSliceBetheBloch( &theTraj, .5, mass , true); + + p_energies_inel.clear(); + p_sliceInts_inel.clear(); + for( size_t islice = 0; islice < thin_slice_inelastic.size(); ++islice ){ + p_energies_inel.push_back( thin_slice_inelastic[islice].first ); + p_sliceInts_inel.push_back( thin_slice_inelastic[islice].second ); + } + + p_energies_el.clear(); + p_sliceInts_el.clear(); + for( size_t islice = 0; islice < thin_slice_elastic.size(); ++islice ){ + p_energies_el.push_back( thin_slice_elastic[islice].first ); + p_sliceInts_el.push_back( thin_slice_elastic[islice].second ); + } + + // Loop through universes (j) + for (size_t j=0; jSetNewVals(UniverseVals.at(j)); + fReweighter->SetNewHists(fParMaker->GetFSHists()); + fReweighter->SetNewElasticHists(fParMaker->GetElasticHist()); + //Get the weight from the G4ReweightTraj + w = fReweighter->GetWeight( &theTraj ); + // Total weight is the product of track weights in the event + weight[j] *= std::max((float)0.0, w); + + // just for the output tree + p_weight[j] = w; + + } // loop through universes (j) + + if (fDebug){ + std::cout << "PDG = " << p_PDG << std::endl; + std::cout << "weights by particle: "; + for (unsigned int j=0; jFill(); + } // loop over mcparticles (i) + if (fMakeOutputTrees) fOutTree_MCTruth->Fill(); + +return weight; + +} + +REGISTER_WEIGHTCALC(Geant4WeightCalc) + +} // namespace evwgh + +} // namespace sbn diff --git a/sbncode/SBNEventWeight/jobs/CMakeLists.txt b/sbncode/SBNEventWeight/jobs/CMakeLists.txt index e36b8b0cc..b5aae6724 100644 --- a/sbncode/SBNEventWeight/jobs/CMakeLists.txt +++ b/sbncode/SBNEventWeight/jobs/CMakeLists.txt @@ -5,4 +5,5 @@ install_source() add_subdirectory(SystTools) add_subdirectory(genie) add_subdirectory(flux) +add_subdirectory(geant4) diff --git a/sbncode/SBNEventWeight/jobs/geant4/CMakeLists.txt b/sbncode/SBNEventWeight/jobs/geant4/CMakeLists.txt new file mode 100644 index 000000000..589bc695a --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/CMakeLists.txt @@ -0,0 +1,6 @@ +install_fhicl() + +FILE(GLOB fcl_files *.fcl) + +install_source( EXTRAS ${fcl_files} ) + diff --git a/sbncode/SBNEventWeight/jobs/geant4/eventweight_geant4_sbn.fcl b/sbncode/SBNEventWeight/jobs/geant4/eventweight_geant4_sbn.fcl new file mode 100644 index 000000000..2431af89d --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/eventweight_geant4_sbn.fcl @@ -0,0 +1,153 @@ +########################################################## +## Hadron reinteraction uncertainties +## +## References: +## +## * K. Duffy, Pion Secondary Interaction Systematics (from GEANTReweight), DocDB 25379 +## * J. Calcutt, GEANTReweight documentation DocDB 25084, repository https://cdcvs.fnal.gov/redmine/projects/geant4reweight/repository +## +## From MicroBooNE, ubsim UBOONE_SUITE_v08_00_00_69 +## (uBooNE author: Kirsty Duffy (kduffy@fnal.gov)) +## +########################################################## + +#include "piplus_reweight_parameters.fcl" +#include "piminus_reweight_parameters.fcl" +#include "proton_reweight_parameters.fcl" +#include "neutron_reweight_parameters.fcl" +#include "kplus_reweight_parameters.fcl" +#include "kminus_reweight_parameters.fcl" + +#include "material_parameters.fcl" + +BEGIN_PROLOG + +sbn_eventweight_geant4: { + module_type: "SBNEventWeight" + AllowMissingTruth: true + min_weight: 0.0 + max_weight: 100 + + weight_functions_reint: [ reinteractions_piplus , reinteractions_piminus, reinteractions_proton , reinteractions_neutron , reinteractions_kplus , reinteractions_kminus ] + + reinteractions_piplus: { + type: Geant4 + material: @local::LAr + random_seed: 58 + parameters: @local::PiPlusParameters + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_piplus.root" + makeoutputtree: true + pdg_to_reweight: 211 + debug: false + } + + reinteractions_piminus: { + type: Geant4 + material: @local::LAr + random_seed: 59 + parameters: @local::PiMinusParameters + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_piminus.root" + makeoutputtree: true + pdg_to_reweight: -211 + debug: false + } + + reinteractions_proton: { + type: Geant4 + material: @local::LAr + random_seed: 60 + parameters: @local::ProtonParameters + #mode: multisigma + #number_of_sigmas: 3 + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_proton.root" # dummy file, cross section pulled directly from Geant4 + makeoutputtree: true + pdg_to_reweight: 2212 + debug: false + } + + reinteractions_neutron: { + type: Geant4 + material: @local::LAr + random_seed: 61 + parameters: @local::NeutronParameters + #mode: multisigma + #number_of_sigmas: 3 + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_proton.root" # dummy file, cross section pulled directly from Geant4 + makeoutputtree: true + pdg_to_reweight: 2112 + debug: false + } + + reinteractions_kplus: { + type: Geant4 + material: @local::LAr + random_seed: 62 + parameters: @local::KPlusParameters + #mode: pm1sigma + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_proton.root" # dummy file, cross section pulled directly from Geant4 + makeoutputtree: true + pdg_to_reweight: 321 + debug: false + } + + reinteractions_kminus: { + type: Geant4 + material: @local::LAr + random_seed: 63 + parameters: @local::KMinusParameters + #mode: pm1sigma + mode: multisim + number_of_multisims: 1000 + fracsfile: "$SBNDATA_DIR/systematics/reint/g4_fracs_proton.root" # dummy file, cross section pulled directly from Geant4 + makeoutputtree: true + pdg_to_reweight: -321 + debug: false + } + +} + +### +# Reweighting parameters should be defined as +# +# TheParameters: [ +# { +# Cut: "reac" +# Name: "fReacLow" +# Range: [10., 200.] +# Nominal: 1.0 +# Sigma: 0.3 +# }, +# {...} +# ] +# +# - Range defines the energy range over which that parameter has an effect (MeV) +# - Nominal is not used in "multisim" mode. In "pm1sigma" mode it defines the +# nominal (around which you calculate +/- 1 sigma variations). In any other +# mode, the parameter is set to the value under Nominal (so it's not really a +# "nominal" value in that case, just the set value) +# - Sigma defines the 1-sigma range for multisims and unisims. Currently the +# code can only accept a single value for sigma, giving symmetric +# uncertainty bounds: nominal-sigma and nominal+sigma +# - Cut must be one of the following: +# "total" <- total scattering cross section, pulled from Geant4 +# "abs" <- absorption cross section (pi+ and pi- only at the moment -- Oct 2019) +# "cex" <- charge exchange cross section (pi+ and pi- only at the moment -- Oct 2019) +# "dcex" <- double charge exchange cross section (pi+ and pi- only at the moment -- Oct 2019) +# "prod" <- pion production cross section (pi+ and pi- only at the moment -- Oct 2019) +# "inel" <- quasi-elastic inelastic scattering cross section (pi+ and pi- only at the moment -- Oct 2019) +# - Name can be anything you like (but should uniquely identify that parameter/ +# variation) +### + +END_PROLOG + diff --git a/sbncode/SBNEventWeight/jobs/geant4/kminus_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/kminus_reweight_parameters.fcl new file mode 100644 index 000000000..ac330892e --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/kminus_reweight_parameters.fcl @@ -0,0 +1,15 @@ +BEGIN_PROLOG + +## KMinus available process keys: "total" + +KMinusParameters: [ + { + Name: "fKMinusTotal" + Cut: "total" + Range: [10.0, 2005.0] + Nominal: 1.0 + Sigma: 0.2 + } +] + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/kplus_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/kplus_reweight_parameters.fcl new file mode 100644 index 000000000..08ec5e9f2 --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/kplus_reweight_parameters.fcl @@ -0,0 +1,15 @@ +BEGIN_PROLOG + +## KPlus available process keys: "total" + +KPlusParameters: [ + { + Name: "fKPlusTotal" + Cut: "total" + Range: [10.0, 2005.0] + Nominal: 1.0 + Sigma: 0.2 + } +] + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/material_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/material_parameters.fcl new file mode 100644 index 000000000..ed735621f --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/material_parameters.fcl @@ -0,0 +1,18 @@ +BEGIN_PROLOG + +# LAr parameters for geant4reweight, stolen out of the ND-Lar 2x2 implementation +# A more complete solution would be to get this info out of the geometry instead... + +LAr: { + Name: "liquidArgon" + Density: 1.390 + Components: [ + { + Z: 18 + Mass: 39.95 + Fraction: 1. + } + ] +} + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/neutron_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/neutron_reweight_parameters.fcl new file mode 100644 index 000000000..65743db98 --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/neutron_reweight_parameters.fcl @@ -0,0 +1,15 @@ +BEGIN_PROLOG + +## Neutron available process keys: "total" + +NeutronParameters: [ + { + Name: "fNeutronTotal" + Cut: "total" + Range: [100.0, 2005.0] + Nominal: 1.0 + Sigma: 0.2 + } +] + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/piminus_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/piminus_reweight_parameters.fcl new file mode 100644 index 000000000..fb27aeed4 --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/piminus_reweight_parameters.fcl @@ -0,0 +1,55 @@ +BEGIN_PROLOG + +## PiMinus available process keys: "abs", "cex", "prod", "dcex", "inel" + +PiMinusParameters: [ + { + Name: "fPiMinusInelLow" + Cut: "inel" + Range: [10., 200.] + Nominal: 1 + Sigma: 0.2 + } , + { + Name: "fPiMinusInelHigh" + Cut: "inel" + Range: [700., 2005.] + Nominal: 1.0 + Sigma: 0.2 + } , + + { + Name: "fPiMinusAbs" + Cut: "abs" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiMinusCex" + Cut: "cex" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiMinusDCex" + Cut: "dcex" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiMinusPiProd" + Cut: "prod" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + } + +] + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/piplus_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/piplus_reweight_parameters.fcl new file mode 100644 index 000000000..4aeaeb96d --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/piplus_reweight_parameters.fcl @@ -0,0 +1,55 @@ +BEGIN_PROLOG + +## PiPlus available process keys: "abs", "cex", "prod", "dcex", "inel" + +PiPlusParameters: [ + { + Name: "fPiPlusInelLow" + Cut: "inel" + Range: [10., 200.] + Nominal: 1.0 + Sigma: 0.2 + } , + { + Name: "fPiPlusInelHigh" + Cut: "inel" + Range: [700., 2005.] + Nominal: 1.0 + Sigma: 0.2 + } , + + { + Name: "fPiPlusAbs" + Cut: "abs" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiPlusCex" + Cut: "cex" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiPlusDCex" + Cut: "dcex" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + }, + + { + Name: "fPiPlusPiProd" + Cut: "prod" + Range: [10.0, 2005.00] + Nominal: 1.0 + Sigma: 0.2 + } + +] + +END_PROLOG diff --git a/sbncode/SBNEventWeight/jobs/geant4/proton_reweight_parameters.fcl b/sbncode/SBNEventWeight/jobs/geant4/proton_reweight_parameters.fcl new file mode 100644 index 000000000..3dd017d93 --- /dev/null +++ b/sbncode/SBNEventWeight/jobs/geant4/proton_reweight_parameters.fcl @@ -0,0 +1,15 @@ +BEGIN_PROLOG + +## Proton available process keys: "total" + +ProtonParameters: [ + { + Name: "fProtonTotal" + Cut: "total" + Range: [10.0, 2005.0] + Nominal: 1.0 + Sigma: 0.2 + } +] + +END_PROLOG diff --git a/sbncode/SinglePhotonAnalysis/CMakeLists.txt b/sbncode/SinglePhotonAnalysis/CMakeLists.txt index 35f24c2bb..1d2a7af6a 100644 --- a/sbncode/SinglePhotonAnalysis/CMakeLists.txt +++ b/sbncode/SinglePhotonAnalysis/CMakeLists.txt @@ -23,21 +23,21 @@ art_make_library( BASENAME_ONLY ROOT::Core ) -cet_build_plugin(NCDeltaRadiative art::module BASENAME_ONLY LIBRARIES +cet_build_plugin(NCDeltaRadiative art::module BASENAME_ONLY LIBRARIES PRIVATE sbncode_SinglePhotonAnalysis_Libraries sbncode_SinglePhotonAnalysis_SEAview art_root_io::TFileService_service ROOT::Tree ) -cet_build_plugin(NCRadiativeResonant art::module BASENAME_ONLY LIBRARIES +cet_build_plugin(NCRadiativeResonant art::module BASENAME_ONLY LIBRARIES PRIVATE sbncode_SinglePhotonAnalysis_Libraries sbncode_SinglePhotonAnalysis_SEAview art_root_io::TFileService_service ROOT::Tree ) -cet_build_plugin(SinglePhoton art::module BASENAME_ONLY LIBRARIES +cet_build_plugin(SinglePhoton art::module BASENAME_ONLY LIBRARIES PRIVATE sbncode_SinglePhotonAnalysis_Libraries sbncode_SinglePhotonAnalysis_SEAview art_root_io::TFileService_service diff --git a/sbncode/SinglePhotonAnalysis/NCDeltaRadiative_module.cc b/sbncode/SinglePhotonAnalysis/NCDeltaRadiative_module.cc index aff90eb39..892d31f06 100644 --- a/sbncode/SinglePhotonAnalysis/NCDeltaRadiative_module.cc +++ b/sbncode/SinglePhotonAnalysis/NCDeltaRadiative_module.cc @@ -161,7 +161,7 @@ bool NCDeltaRadiative::filter(art::Event & e) { simb::MCParticle const & mcp = mct.GetParticle(i); if(mcp.TrackId() != i) { std::cout << "ERROR: " << __LINE__ << " " << __PRETTY_FUNCTION__ << "\nTrackId does not match index\n"; - exit(1); + return false; } if(!(mcp.StatusCode() == 1 && mcp.PdgCode() == 22)) continue; exiting_photon_parents.push_back(mcp.Mother()); @@ -173,7 +173,7 @@ bool NCDeltaRadiative::filter(art::Event & e) { //CHEKC hardcode, TPC filter: if(abs(mcp.Vx())>210 || abs(mcp.Vy())>210||mcp.Vz()>510 || mcp.Vz()<-1){ std::cout<<"OUTSIDE TPC x y z ="<210 || abs(mcp.Vy())>210||mcp.Vz()>510 || mcp.Vz()<-1){ std::cout<<"OUTSIDE TPC x y z ="< -genie_xsec v3_04_00 - +genie_xsec v3_06_00 - larcv2 v2_2_6 - -larsoft v10_04_06 - -sbnanaobj v10_00_00 - +larsoft v10_22_00 - +sbnalg v10_22_00 - sbndaq_artdaq_core v1_10_06 - -sbndata v01_07 - -sbnobj v10_00_07 - -systematicstools v01_04_04 - -nusystematics v1_05_04 - +sbndata v01_10 - +systematicstools v01_04_05 - +nusystematics v1_05_14 - +geant4reweight v01_21_03 - cetmodules v3_24_01 - only_for_build end_product_list #################################### @@ -316,12 +316,11 @@ end_product_list # case it is optional. # #################################### - -qualifier larsoft sbnobj sbnanaobj sbndaq_artdaq_core genie_xsec sbndata larcv2 systematicstools nusystematics notes -c14:debug c14:debug c14:debug c14:debug c14:s131:debug AR2320i00000:e1000:k250 -nq- c14:debug:p3915 c14:debug c14:debug -nq- -c14:prof c14:prof c14:prof c14:prof c14:s131:prof AR2320i00000:e1000:k250 -nq- c14:p3915:prof c14:prof c14:prof -nq- -e26:debug e26:debug e26:debug e26:debug e26:s131:debug AR2320i00000:e1000:k250 -nq- debug:e26:p3915 e26:debug e26:debug -nq- -e26:prof e26:prof e26:prof e26:prof e26:s131:prof AR2320i00000:e1000:k250 -nq- e26:p3915:prof e26:prof e26:prof -nq- +qualifier larsoft sbnalg sbndaq_artdaq_core genie_xsec sbndata larcv2 systematicstools nusystematics geant4reweight notes +c14:debug c14:debug c14:debug c14:s131:debug AR2320i00000:e1000:k250 -nq- c14:debug:p3915 c14:debug c14:debug c14:debug:s131 -nq- +c14:prof c14:prof c14:prof c14:s131:prof AR2320i00000:e1000:k250 -nq- c14:p3915:prof c14:prof c14:prof c14:prof:s131 -nq- +e26:debug e26:debug e26:debug e26:s131:debug AR2320i00000:e1000:k250 -nq- debug:e26:p3915 e26:debug e26:debug e26:debug:s131 -nq- +e26:prof e26:prof e26:prof e26:s131:prof AR2320i00000:e1000:k250 -nq- e26:p3915:prof e26:prof e26:prof e26:prof:s131 -nq- end_qualifier_list ####################################