Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-26 07:50:05

0001 #include <algorithm>
0002 #include <array>
0003 #include <cstdlib>
0004 #include <filesystem>
0005 #include <fstream>
0006 #include <iostream>
0007 #include <ranges>
0008 #include <sstream>
0009 #include <stdexcept>
0010 #include <string>
0011 #include <string_view>
0012 #include <sys/stat.h>
0013 #include <vector>
0014 
0015 #include <cuda_runtime.h>
0016 #include <nlohmann/json.hpp>
0017 
0018 #include "SEventConfig.hh"
0019 
0020 #include "config.h"
0021 #include "config_path.h"
0022 
0023 namespace gphox
0024 {
0025 
0026 using namespace std;
0027 
0028 constexpr const char* GPHOX_PTX_PATH_ENV = "CSGOptiX__optixpath";
0029 
0030 namespace
0031 {
0032 
0033 template <typename Enum>
0034 struct NamedEnumInfo
0035 {
0036     Enum             value;
0037     std::string_view name;
0038 };
0039 
0040 inline constexpr std::array EventModeInfos{
0041     NamedEnumInfo<EventMode>{EventMode::DebugHeavy, "DebugHeavy"},
0042     NamedEnumInfo<EventMode>{EventMode::DebugLite, "DebugLite"},
0043     NamedEnumInfo<EventMode>{EventMode::Nothing, "Nothing"},
0044     NamedEnumInfo<EventMode>{EventMode::Minimal, "Minimal"},
0045     NamedEnumInfo<EventMode>{EventMode::Hit, "Hit"},
0046     NamedEnumInfo<EventMode>{EventMode::HitPhoton, "HitPhoton"},
0047     NamedEnumInfo<EventMode>{EventMode::HitPhotonSeq, "HitPhotonSeq"},
0048     NamedEnumInfo<EventMode>{EventMode::HitSeq, "HitSeq"},
0049 };
0050 
0051 inline constexpr std::array TorchGentypeInfos{
0052     NamedEnumInfo<unsigned>{OpticksGenstep_TORCH, "TORCH"},
0053 };
0054 
0055 inline constexpr std::array TorchTypeInfos{
0056     NamedEnumInfo<unsigned>{T_DISC, "disc"},
0057     NamedEnumInfo<unsigned>{T_LINE, "line"},
0058     NamedEnumInfo<unsigned>{T_POINT, "point"},
0059     NamedEnumInfo<unsigned>{T_CIRCLE, "circle"},
0060     NamedEnumInfo<unsigned>{T_RECTANGLE, "rectangle"},
0061     NamedEnumInfo<unsigned>{T_SPHERE_MARSAGLIA, "sphere_marsaglia"},
0062     NamedEnumInfo<unsigned>{T_SPHERE, "sphere"},
0063 };
0064 
0065 inline constexpr std::array ModeLiteInfos{
0066     NamedEnumInfo<ModeLite>{ModeLite::Unspecified, "Unspecified"},
0067     NamedEnumInfo<ModeLite>{ModeLite::Standard, "Standard"},
0068     NamedEnumInfo<ModeLite>{ModeLite::Lite, "Lite"},
0069     NamedEnumInfo<ModeLite>{ModeLite::DebugCompare, "DebugCompare"},
0070 };
0071 
0072 inline constexpr std::array ModeMergeInfos{
0073     NamedEnumInfo<ModeMerge>{ModeMerge::Unspecified, "Unspecified"},
0074     NamedEnumInfo<ModeMerge>{ModeMerge::Separate, "Separate"},
0075     NamedEnumInfo<ModeMerge>{ModeMerge::Merged, "Merged"},
0076 };
0077 
0078 bool FileExists(const std::string& path)
0079 {
0080     if (path.empty())
0081         return false;
0082     std::error_code ec;
0083     return std::filesystem::exists(path, ec) && !ec;
0084 }
0085 
0086 std::vector<std::string> SplitSearchPaths(std::string_view paths)
0087 {
0088     std::vector<std::string> search_paths;
0089 
0090     size_t last = 0;
0091     size_t next = 0;
0092     while ((next = paths.find(':', last)) != std::string_view::npos)
0093     {
0094         search_paths.push_back(std::string{paths.substr(last, next - last)});
0095         last = next + 1;
0096     }
0097 
0098     search_paths.push_back(std::string{paths.substr(last)});
0099     return search_paths;
0100 }
0101 
0102 template <typename Enum, size_t N>
0103 std::string ValidNamedEnumNames(const std::array<NamedEnumInfo<Enum>, N>& infos)
0104 {
0105     std::string names;
0106     for (const auto& info : infos)
0107     {
0108         if (!names.empty())
0109             names += ", ";
0110         names += info.name;
0111     }
0112     return names;
0113 }
0114 
0115 template <typename Enum, size_t N>
0116 std::string_view NamedEnumName(
0117     Enum                                      value,
0118     const std::array<NamedEnumInfo<Enum>, N>& infos,
0119     std::string_view                          fallback)
0120 {
0121     const auto it = std::ranges::find(infos, value, &NamedEnumInfo<Enum>::value);
0122     if (it != infos.end())
0123         return it->name;
0124 
0125     return fallback;
0126 }
0127 
0128 template <typename Enum, size_t N>
0129 Enum ReadNamedEnum(
0130     const nlohmann::json&                     object,
0131     const char*                               section,
0132     const char*                               key,
0133     const std::array<NamedEnumInfo<Enum>, N>& infos)
0134 {
0135     const std::string full_key = std::string{section} + "." + key;
0136     const auto&       value = object.at(key);
0137     const std::string valid_names = ValidNamedEnumNames(infos);
0138 
0139     if (!value.is_string())
0140         throw std::invalid_argument{"Invalid " + full_key + " value. Expected string name, one of: " + valid_names};
0141 
0142     const std::string name = value.get<std::string>();
0143     const auto        it = std::ranges::find(infos, std::string_view{name}, &NamedEnumInfo<Enum>::name);
0144     if (it != infos.end())
0145         return it->value;
0146 
0147     throw std::invalid_argument{"Invalid " + full_key + " \"" + name + "\". Expected one of: " + valid_names};
0148 }
0149 
0150 std::string_view EventModeName(EventMode mode)
0151 {
0152     return NamedEnumName(mode, EventModeInfos, "Minimal");
0153 }
0154 
0155 template <typename T>
0156 void Assign(const nlohmann::json& object, const char* key, T& value)
0157 {
0158     if (const auto it = object.find(key); it != object.end())
0159         value = it->get<T>();
0160 }
0161 
0162 void AssignFloat2(const nlohmann::json& object, const char* key, float2& value)
0163 {
0164     if (const auto it = object.find(key); it != object.end())
0165         value = make_float2((*it)[0].get<float>(), (*it)[1].get<float>());
0166 }
0167 
0168 void AssignFloat3(const nlohmann::json& object, const char* key, float3& value)
0169 {
0170     if (const auto it = object.find(key); it != object.end())
0171         value = make_float3((*it)[0].get<float>(), (*it)[1].get<float>(), (*it)[2].get<float>());
0172 }
0173 
0174 void AssignNormalizedFloat3(const nlohmann::json& object, const char* key, float3& value)
0175 {
0176     if (const auto it = object.find(key); it != object.end())
0177     {
0178         const float3 candidate = make_float3(
0179             (*it)[0].get<float>(),
0180             (*it)[1].get<float>(),
0181             (*it)[2].get<float>());
0182         constexpr float epsilon = 1e-12f;
0183         const float     length_squared = candidate.x * candidate.x + candidate.y * candidate.y + candidate.z * candidate.z;
0184         if (length_squared <= epsilon)
0185             throw std::invalid_argument(std::string("Cannot normalize vector for key '") + key + "'");
0186 
0187         value = normalize(candidate);
0188     }
0189 }
0190 
0191 template <typename Enum, size_t N>
0192 void AssignNamedEnum(
0193     const nlohmann::json&                     object,
0194     const char*                               section,
0195     const char*                               key,
0196     Enum&                                     value,
0197     const std::array<NamedEnumInfo<Enum>, N>& infos)
0198 {
0199     if (object.contains(key))
0200         value = ReadNamedEnum(object, section, key, infos);
0201 }
0202 
0203 void AssignOutputDir(const nlohmann::json& event, std::filesystem::path& output_dir)
0204 {
0205     if (const auto it = event.find("output_dir"); it != event.end())
0206         output_dir = it->get<std::string>();
0207 }
0208 
0209 } // namespace
0210 
0211 Config::Config(std::string config_name) :
0212     name{config_name}
0213 {
0214     ReadConfig(Locate(name + ".json"));
0215     Apply();
0216 }
0217 
0218 std::string Config::PtxPath(const std::string& ptx_name)
0219 {
0220     const char* env_path = std::getenv(GPHOX_PTX_PATH_ENV);
0221     if (env_path && FileExists(env_path))
0222         return env_path;
0223 
0224     std::vector<std::string> candidates;
0225     for (const auto& dir : SplitSearchPaths(GPHOX_PTX_SEARCH_PATHS))
0226     {
0227         if (dir.empty())
0228             continue;
0229 
0230         std::string candidate = (std::filesystem::path{dir} / ptx_name).string();
0231         candidates.push_back(candidate);
0232         if (FileExists(candidate))
0233             return candidate;
0234     }
0235 
0236     std::stringstream errmsg;
0237     errmsg << "Could not resolve PTX file \"" << ptx_name << "\".\n"
0238            << "Expected one of:\n"
0239            << "  - " << GPHOX_PTX_PATH_ENV << "=<path-to-ptx>\n";
0240     for (const auto& candidate : candidates)
0241         errmsg << "  - " << candidate << "\n";
0242     throw std::runtime_error(errmsg.str());
0243 }
0244 
0245 std::string Config::Locate(std::string filename) const
0246 {
0247     std::vector<std::string> search_paths;
0248 
0249     const std::string user_dir{std::getenv("GPHOX_CONFIG_DIR") ? std::getenv("GPHOX_CONFIG_DIR") : ""};
0250 
0251     if (user_dir.empty())
0252     {
0253         search_paths = SplitSearchPaths(GPHOX_CONFIG_SEARCH_PATHS);
0254     }
0255     else
0256     {
0257         search_paths.push_back(user_dir);
0258     }
0259 
0260     struct stat buffer;
0261     std::string filepath{""};
0262     for (std::string path : search_paths)
0263     {
0264         std::string fpath{path + "/" + filename};
0265         if (stat(fpath.c_str(), &buffer) == 0)
0266         {
0267             filepath = fpath;
0268             break;
0269         }
0270     }
0271 
0272     if (filepath.empty())
0273     {
0274         std::string errmsg{"Could not find config file \"" + filename + "\" in "};
0275         for (std::string path : search_paths) errmsg += (path + ":");
0276         throw std::runtime_error(errmsg);
0277     }
0278 
0279     return filepath;
0280 }
0281 
0282 /**
0283  * Expects a valid filepath.
0284  */
0285 void Config::ReadConfig(std::string filepath)
0286 {
0287     nlohmann::json json;
0288 
0289     try
0290     {
0291         std::ifstream ifs(filepath);
0292         ifs >> json;
0293 
0294         if (const auto it = json.find("torch"); it != json.end())
0295         {
0296             const nlohmann::json& torch_ = *it;
0297 
0298             AssignNamedEnum(torch_, "torch", "gentype", torch.gentype, TorchGentypeInfos);
0299             Assign(torch_, "trackid", torch.trackid);
0300             Assign(torch_, "matline", torch.matline);
0301             Assign(torch_, "numphoton", torch.numphoton);
0302             AssignFloat3(torch_, "pos", torch.pos);
0303             Assign(torch_, "time", torch.time);
0304             AssignNormalizedFloat3(torch_, "mom", torch.mom);
0305             Assign(torch_, "weight", torch.weight);
0306             AssignFloat3(torch_, "pol", torch.pol);
0307             Assign(torch_, "wavelength", torch.wavelength);
0308             AssignFloat2(torch_, "zenith", torch.zenith);
0309             AssignFloat2(torch_, "azimuth", torch.azimuth);
0310             Assign(torch_, "radius", torch.radius);
0311             Assign(torch_, "distance", torch.distance);
0312             Assign(torch_, "mode", torch.mode);
0313             AssignNamedEnum(torch_, "torch", "type", torch.type, TorchTypeInfos);
0314         }
0315 
0316         if (const auto it = json.find("event"); it != json.end())
0317         {
0318             const nlohmann::json& event_ = *it;
0319 
0320             Assign(event_, "max_bounce", max_bounce);
0321             Assign(event_, "max_genstep", max_genstep);
0322             Assign(event_, "maxslot", maxslot);
0323             AssignNamedEnum(event_, "event", "mode", event_mode, EventModeInfos);
0324             AssignNamedEnum(event_, "event", "mode_lite", mode_lite, ModeLiteInfos);
0325             AssignNamedEnum(event_, "event", "mode_merge", mode_merge, ModeMergeInfos);
0326             AssignOutputDir(event_, output_dir);
0327             Assign(event_, "propagate_epsilon", propagate_epsilon);
0328             Assign(event_, "propagate_epsilon0", propagate_epsilon0);
0329             Assign(event_, "propagate_epsilon0_mask", propagate_epsilon0_mask);
0330         }
0331     }
0332     catch (nlohmann::json::exception& e)
0333     {
0334         throw std::runtime_error{"Failed reading config parameters from " + filepath + "\n" + e.what()};
0335     }
0336     catch (std::invalid_argument& e)
0337     {
0338         throw std::runtime_error{"Invalid config value in " + filepath + "\n" + e.what()};
0339     }
0340 }
0341 
0342 void Config::Apply() const
0343 {
0344     const std::string event_mode_name{EventModeName(event_mode)};
0345     const std::string output_dir_str = output_dir.string();
0346 
0347     SEventConfig::SetMaxBounce(max_bounce);
0348     SEventConfig::SetMaxGenstep(max_genstep);
0349     SEventConfig::SetMaxSlot(maxslot);
0350     SEventConfig::SetEventMode(event_mode_name.c_str());
0351     if (mode_lite != ModeLite::Unspecified)
0352         SEventConfig::SetModeLite(static_cast<int>(mode_lite));
0353     if (mode_merge != ModeMerge::Unspecified)
0354         SEventConfig::SetModeMerge(static_cast<int>(mode_merge));
0355     SEventConfig::SetOutFold(output_dir_str.c_str());
0356     SEventConfig::SetPropagateEpsilon(propagate_epsilon);
0357     SEventConfig::SetPropagateEpsilon0(propagate_epsilon0);
0358     SEventConfig::SetPropagateEpsilon0Mask(propagate_epsilon0_mask.c_str());
0359 }
0360 
0361 } // namespace gphox