Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:15:24

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