Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:17:51

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022, 2023 Whitney Armstrong, Wouter Deconinck, David Lawrence
0003 //
0004 
0005 #include <JANA/JApplication.h>
0006 #include <JANA/JException.h>
0007 #include <JANA/Services/JServiceLocator.h>
0008 #include <Parsers/Printout.h>
0009 #include <TGeoManager.h>
0010 #include <fmt/color.h>
0011 #include <fmt/core.h>
0012 #include <fmt/format.h>
0013 #include <cstdlib>
0014 #include <exception>
0015 #include <filesystem>
0016 #include <iostream>
0017 #include <stdexcept>
0018 #include <utility>
0019 #include <vector>
0020 
0021 #include "DD4hep_service.h"
0022 #include "services/log/Log_service.h"
0023 
0024 //----------------------------------------------------------------
0025 // Services
0026 //----------------------------------------------------------------
0027 void DD4hep_service::acquire_services(JServiceLocator* srv_locator) {
0028   // logging service
0029   auto log_service = srv_locator->get<Log_service>();
0030   m_log            = log_service->logger("dd4hep");
0031 
0032   // Set the DD4hep print level to be quieter by default, but let user adjust it
0033   std::string print_level_str{"WARNING"};
0034   m_app->SetDefaultParameter("dd4hep:print_level", print_level_str,
0035                              "Set DD4hep print level (see DD4hep/Printout.h)");
0036   dd4hep::setPrintLevel(dd4hep::decodePrintLevel(print_level_str));
0037 
0038   // Set the TGeoManager verbose level (lower dd4hep level is more verbose)
0039   TGeoManager::SetVerboseLevel(dd4hep::printLevel() <= dd4hep::PrintLevel::INFO ? 1 : 0);
0040 }
0041 
0042 //----------------------------------------------------------------
0043 // destructor
0044 //----------------------------------------------------------------
0045 DD4hep_service::~DD4hep_service() {
0046   try {
0047     if (m_dd4hepGeo) {
0048       m_dd4hepGeo->destroyInstance();
0049     }
0050     m_dd4hepGeo = nullptr;
0051   } catch (...) {
0052   }
0053 }
0054 
0055 //----------------------------------------------------------------
0056 // detector
0057 //
0058 /// Return pointer to the dd4hep::Detector object.
0059 /// Call Initialize if needed.
0060 //----------------------------------------------------------------
0061 gsl::not_null<const dd4hep::Detector*> DD4hep_service::detector() {
0062   std::call_once(init_flag, &DD4hep_service::Initialize, this);
0063   return m_dd4hepGeo.get();
0064 }
0065 
0066 //----------------------------------------------------------------
0067 // converter
0068 //
0069 /// Return pointer to the cellIDPositionConverter object.
0070 /// Call Initialize if needed.
0071 //----------------------------------------------------------------
0072 gsl::not_null<const dd4hep::rec::CellIDPositionConverter*> DD4hep_service::converter() {
0073   std::call_once(init_flag, &DD4hep_service::Initialize, this);
0074   return m_cellid_converter.get();
0075 }
0076 
0077 //----------------------------------------------------------------
0078 // Initialize
0079 //
0080 /// Initialize the dd4hep geometry by reading in from the XML.
0081 /// Note that this is called automatically the first time detector()
0082 /// is called. Which XML file(s) are read is determined by the
0083 /// dd4hep:xml_files configuration parameter.
0084 //----------------------------------------------------------------
0085 void DD4hep_service::Initialize() {
0086 
0087   if (m_dd4hepGeo) {
0088     m_log->warn("DD4hep_service already initialized!");
0089   }
0090 
0091   // The current recommended way of getting the XML file is to use the environment variables
0092   // DETECTOR_PATH and DETECTOR_CONFIG.
0093   // Look for those first, so we can use it for the default
0094   // config parameter.
0095   auto* detector_config_env = std::getenv("DETECTOR_CONFIG");
0096   auto* detector_path_env   = std::getenv("DETECTOR_PATH");
0097 
0098   std::string detector_config;
0099   // Check if detector_config_env is set
0100   if (detector_config_env != nullptr) {
0101     detector_config = detector_config_env;
0102   }
0103 
0104   // do we have default file name
0105   if (!detector_config.empty()) {
0106     m_xml_files.push_back(std::string(detector_path_env != nullptr ? detector_path_env : ".") +
0107                           "/" + detector_config + ".xml");
0108   }
0109 
0110   // User may specify multiple geometry files via the config. parameter. Normally, this
0111   // will be a single file which itself has includes for other files.
0112   m_app->SetDefaultParameter("dd4hep:xml_files", m_xml_files,
0113                              "Comma separated list of XML files describing the DD4hep geometry. "
0114                              "(Defaults to ${DETECTOR_PATH}/${DETECTOR_CONFIG}.xml using envars.)");
0115 
0116   if (m_xml_files.empty()) {
0117     m_log->error("No dd4hep XML file specified for the geometry!");
0118     m_log->error("Set your DETECTOR_PATH and DETECTOR_CONFIG environment variables");
0119     m_log->error("(the latter is typically done by sourcing the thisepic.sh");
0120     m_log->error("script the epic directory.)");
0121     throw std::runtime_error("No dd4hep XML file specified.");
0122   }
0123 
0124   // Reading the geometry may take a long time and if the JANA ticker is enabled, it will keep printing
0125   // while no other output is coming which makes it look like something is wrong. Disable the ticker
0126   // while parsing and loading the geometry
0127   auto tickerEnabled = m_app->IsTickerEnabled();
0128   m_app->SetTicker(false);
0129 
0130   // load geometry
0131   auto detector = dd4hep::Detector::make_unique("");
0132   try {
0133     m_log->info("Loading DD4hep geometry from {} files", m_xml_files.size());
0134     for (auto& filename : m_xml_files) {
0135 
0136       auto resolved_filename = resolveFileName(filename, detector_path_env);
0137 
0138       m_log->info("  - loading geometry file:  '{}' (patience ....)", resolved_filename);
0139       try {
0140         detector->fromCompact(resolved_filename);
0141       } catch (
0142           std::runtime_error& e) { // dd4hep throws std::runtime_error, no way to detail further
0143         throw JException(e.what());
0144       }
0145     }
0146     detector->volumeManager();
0147     detector->apply("DD4hepVolumeManager", 0, nullptr);
0148     m_cellid_converter = std::make_unique<const dd4hep::rec::CellIDPositionConverter>(*detector);
0149     m_dd4hepGeo        = std::move(detector); // const
0150 
0151     m_log->info("Geometry successfully loaded.");
0152   } catch (std::exception& e) {
0153     m_log->error("Problem loading geometry: {}", e.what());
0154     throw std::runtime_error(fmt::format("Problem loading geometry: {}", e.what()));
0155   }
0156 
0157   // Restore the ticker setting
0158   m_app->SetTicker(tickerEnabled);
0159 }
0160 
0161 std::string DD4hep_service::resolveFileName(const std::string& filename, char* detector_path_env) {
0162 
0163   std::string result(filename);
0164 
0165   // Check that this XML file actually exists.
0166   if (!std::filesystem::exists(result)) {
0167 
0168     // filename does not exist, maybe DETECTOR_PATH/filename is meant?
0169     if (detector_path_env != nullptr) {
0170 
0171       // Try looking filename in DETECTOR_PATH
0172       result = std::string(detector_path_env) + "/" + filename;
0173 
0174       if (!std::filesystem::exists(result)) {
0175         // Here we go against the standard practice of throwing an error and print
0176         // the message and exit immediately. This is because we want the last message
0177         // on the screen to be that this file doesn't exist.
0178         auto mess = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), "ERROR: ");
0179         mess += fmt::format(fmt::emphasis::bold, "file: {} does not exist!", filename);
0180         mess += "\nCheck that your DETECTOR_PATH and DETECTOR_CONFIG environment variables are set "
0181                 "correctly.";
0182         std::cerr << std::endl
0183                   << std::endl
0184                   << mess << std::endl
0185                   << std::endl; // TODO standard log here!
0186         std::_Exit(EXIT_FAILURE);
0187       }
0188     }
0189   }
0190   return result;
0191 }