From ef6fd5f16880e7c9b347f9032723828e0866c1e0 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Fri, 1 Mar 2024 14:24:17 +0100 Subject: [PATCH] First attempt at dlopen-based physics list plugins --- .../pkgs/G4/G4Launcher/libsrc/Launcher.cc | 16 +-- .../pkgs/G4/G4Launcher/python/_launcher.py | 27 ++-- .../data/plugin_g4physlist_Empty.txt | 1 + .../G4/G4PhysicsLists/libinc/PhysListMgr.hh | 20 +++ .../libinc/PhysicsListFactory.hh | 21 ---- .../G4/G4PhysicsLists/libsrc/PhysListMgr.cc | 116 ++++++++++++++++++ .../G4PhysicsLists/libsrc/PhysicsListEmpty.cc | 7 ++ .../libsrc/PhysicsListFactory.cc | 47 ------- .../libsrc/PhysicsListFactory_py.cc | 17 --- .../pkgs/G4/G4PhysicsLists/pycpp__init/mod.cc | 38 ++++-- .../pycpp_g4physlist_Empty/mod.cc | 7 -- .../pkgs/G4/G4PhysicsLists/python/__init__.py | 60 +-------- 12 files changed, 195 insertions(+), 182 deletions(-) create mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/data/plugin_g4physlist_Empty.txt create mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysListMgr.hh delete mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysicsListFactory.hh create mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysListMgr.cc delete mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory.cc delete mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory_py.cc delete mode 100644 src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp_g4physlist_Empty/mod.cc diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/libsrc/Launcher.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/libsrc/Launcher.cc index c0dffa3..454610a 100644 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/libsrc/Launcher.cc +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/libsrc/Launcher.cc @@ -7,7 +7,7 @@ #include "Core/String.hh" #include "G4Launcher/Launcher.hh" #include "G4Launcher/SingleParticleGun.hh" -#include "G4PhysicsLists/PhysicsListFactory.hh" +#include "G4PhysicsLists/PhysListMgr.hh" #include "G4Interfaces/GeoConstructBase.hh" #include "G4Interfaces/ParticleGenBase.hh" #include "G4Interfaces/StepFilterBase.hh" @@ -431,19 +431,7 @@ void G4Launcher::Launcher::Imp::preinit() } } - //A bit brute-force and inefficient, but no biggie: - std::vector reflists; - PhysicsListFactory::getAllReferenceListNames(reflists); - for (auto it = reflists.begin();it!=reflists.end();++it) { - if (*it==pln_parts.front()) { - pl = PhysicsListFactory::createReferencePhysicsList(pln_parts.front()); - assert(pl); - } - } - if (!pl) { - //Our own custom list which was simply specified via a string name rather than a provider? - pl = PhysicsListFactory::attemptCreateCustomPhysicsList(pln_parts.front()); - } + pl = PhysListMgr::createList( pln_parts.front() ); if (!pl) { printf("%sUnknown physics list: %s\n",prefix(),pln_parts.front().c_str()); error("Physics list name is not known"); diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/python/_launcher.py b/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/python/_launcher.py index b93b72f..cc78aeb 100644 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/python/_launcher.py +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4Launcher/python/_launcher.py @@ -583,19 +583,20 @@ def create_density_map(): self._shutdown()#shutdown: Make sure run-manager deletion happens now and not when garbage collection runs -_setPhysicsList_orig = Launcher.setPhysicsList -def _setPhysicsList(self,physlistname): - #Make sure that custom physics lists will be translated into a provider - #rather than just the string (this assumes that the default physics list - #will not be custom, which it can't be if we want to avoid a direct - #dependency on the custom physics list code in question). - import G4PhysicsLists - if G4PhysicsLists.listIsCustom(physlistname): - #extract physics list provider and apply that: - self.setPhysicsListProvider(G4PhysicsLists.extractProvider(physlistname)) - else: - _setPhysicsList_orig(self,physlistname) +##_setPhysicsList_orig = Launcher.setPhysicsList +##def _setPhysicsList(self,physlistname): +## #Make sure that custom physics lists will be translated into a provider +## #rather than just the string (this assumes that the default physics list +## #will not be custom, which it can't be if we want to avoid a direct +## #dependency on the custom physics list code in question). +## import G4PhysicsLists +## if G4PhysicsLists.listIsCustom(physlistname): +## #extract physics list provider and apply that: +## self.setPhysicsListProvider(G4PhysicsLists.extractProvider(physlistname)) +## else: +## _setPhysicsList_orig(self,physlistname) +## +#Launcher.setPhysicsList = _setPhysicsList -Launcher.setPhysicsList = _setPhysicsList Launcher.swallowCmdLineAndLaunch = _swallowCmdLineAndLaunch Launcher.go = _swallowCmdLineAndLaunch#alias diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/data/plugin_g4physlist_Empty.txt b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/data/plugin_g4physlist_Empty.txt new file mode 100644 index 0000000..f95458e --- /dev/null +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/data/plugin_g4physlist_Empty.txt @@ -0,0 +1 @@ +Empty physics list. diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysListMgr.hh b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysListMgr.hh new file mode 100644 index 0000000..4ec66ef --- /dev/null +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysListMgr.hh @@ -0,0 +1,20 @@ +#ifndef dgcode_PhysListMgr_hh +#define dgcode_PhysListMgr_hh + +#include +#include +class G4VUserPhysicsList; + +namespace PhysListMgr { + struct PhysListInfo { + std::string name; + std::string pkg_name;//not set for g4 ref lists + std::string description;//only set if loaded. + }; + enum class LoadDescriptions { YES, NO }; + std::vector getAllLists( LoadDescriptions + = LoadDescriptions::NO ); + G4VUserPhysicsList * createList( std::string name ); +} + +#endif diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysicsListFactory.hh b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysicsListFactory.hh deleted file mode 100644 index 4b95c10..0000000 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libinc/PhysicsListFactory.hh +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef G4PhysicsLists_PhysicsListFactory_hh -#define G4PhysicsLists_PhysicsListFactory_hh - -#include -#include -class G4VUserPhysicsList; - -//This is a physics list factory which can be used to detect available G4 -//reference physics lists and to instantiate them. -// -//NOTE: Custom "home-made" physics lists should no longer be added here, they -//will be detected and instantiated on the python-side. - -class PhysicsListFactory { -public: - static G4VUserPhysicsList * createReferencePhysicsList(const std::string& name); - static void getAllReferenceListNames(std::vector&); - static G4VUserPhysicsList * attemptCreateCustomPhysicsList(const std::string& name); -}; - -#endif diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysListMgr.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysListMgr.cc new file mode 100644 index 0000000..a84a99f --- /dev/null +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysListMgr.cc @@ -0,0 +1,116 @@ +#include "G4PhysicsLists/PhysListMgr.hh" +#include "NCrystal/internal/NCStrView.hh" +#include +#include "G4PhysListFactory.hh" +#include "PluginUtils/PluginHelper.hh" + +namespace PhysListMgr { + namespace { + struct dgcode_PluginDef_G4PL { + using plugin_fct_signature_t = G4VUserPhysicsList *(); + static constexpr const char * plugin_type = "g4physlist"; + }; + + G4VUserPhysicsList * + createReferencePhysicsList(const std::string& name) + { + //G4 provided reference lists: + G4PhysListFactory reflist_factory; + return reflist_factory.GetReferencePhysList(name); + } + + std::vector getAllReferenceListNames() + { + //G4 provided reference lists: + G4PhysListFactory reflist_factory; + auto refpl_buggy = reflist_factory.AvailablePhysLists(); + //alternative endings for EM physics: + auto refplEM = reflist_factory.AvailablePhysListsEM(); + + //Workaround bug in G4PhysListFactory: QGSP is too much, ShieldingLEND is + //missing. Also, ShieldingLEND should only be there when G4LENDDATA is + //set. + static const std::string envG4LENDDATA = []() { + const char * envG4LENDDATA_cstr = std::getenv("G4LENDDATA"); + return std::string( envG4LENDDATA_cstr ? envG4LENDDATA_cstr : "" ); + }(); + + bool allowLEND = !envG4LENDDATA.empty(); + + std::vector refpl; + bool sawShieldingLEND = false; + for (auto it = refpl_buggy.begin();it != refpl_buggy.end();++it) { + if (*it=="QGSP") + continue; + if (*it=="ShieldingLEND") { + sawShieldingLEND = true; + if (allowLEND) + refpl.push_back(*it); + } else { + refpl.push_back(*it); + } + } + if (allowLEND&&!sawShieldingLEND) + refpl.push_back("ShieldingLEND"); + + //combine hadronic and em parts (a large number admittedly): + std::vector v; + v.reserve(128); + for (auto it = refpl.begin();it != refpl.end();++it) { + for (auto itEM = refplEM.begin();itEM!= refplEM.end();++itEM) + v.push_back(*it+*itEM); + } + return v; + } + } +} + +std::vector +PhysListMgr::getAllLists( LoadDescriptions load_descr_enum ) +{ + bool load_descr( load_descr_enum==LoadDescriptions::YES ); + auto load_descr_ph( load_descr + ? PluginHelper::LoadDescriptions::YES + : PluginHelper::LoadDescriptions::NO ); + std::vector res; + + //First the G4 ref lists: + const std::string g4reflistdescr("Geant4 reference list"); + for ( auto& reflistname : getAllReferenceListNames() ) { + if ( NCrystal::StrView( reflistname ).startswith("PL_") ) + throw std::runtime_error("g4 ref lists are assumed" + " to never start with \"PL_\""); + res.emplace_back(); + auto& e = res.back(); + e.name = reflistname; + if ( load_descr ) + e.description = g4reflistdescr; + } + //Then the custom plugins: + + for ( auto & plugininfo : PluginHelper::getAvailablePlugins + ( load_descr_ph ) ) + { + res.emplace_back(); + auto& e = res.back(); + e.name = std::string("PL_")+plugininfo.plugin_key; + e.pkg_name = plugininfo.pkgname; + if ( plugininfo.description.has_value() ) + e.description = plugininfo.description.value(); + } + return res; +} + +G4VUserPhysicsList * PhysListMgr::createList( std::string name ) +{ + NCrystal::StrView name_sv(name); + if ( name_sv.startswith("PL_") ) { + //A custom plugins: + name_sv = name_sv.substr(3);//discard "PL_" + auto pl_fct + = PluginHelper::loadPlugin(name_sv.to_string()); + return pl_fct(); + } else { + return createReferencePhysicsList( name ); + } +} diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListEmpty.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListEmpty.cc index 222a3e8..67227b0 100644 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListEmpty.cc +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListEmpty.cc @@ -35,3 +35,10 @@ void PhysicsListEmpty::ConstructProcess() } + +extern "C" { + G4VUserPhysicsList * sbldplugindef_g4physlist_Empty() + { + return new PhysicsListEmpty; + } +} diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory.cc deleted file mode 100644 index ade106b..0000000 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory.cc +++ /dev/null @@ -1,47 +0,0 @@ -#include "G4PhysicsLists/PhysicsListFactory.hh" -#include "G4PhysListFactory.hh" -#include - -G4VUserPhysicsList * PhysicsListFactory::createReferencePhysicsList(const std::string& name) -{ - //G4 provided reference lists: - G4PhysListFactory reflist_factory; - return reflist_factory.GetReferencePhysList(name); -} - -void PhysicsListFactory::getAllReferenceListNames(std::vector& v) -{ - v.clear(); - //G4 provided reference lists: - G4PhysListFactory reflist_factory; - auto refpl_buggy = reflist_factory.AvailablePhysLists(); - auto refplEM = reflist_factory.AvailablePhysListsEM();//alternative endings for EM physics. - - //Workaround bug in G4PhysListFactory: QGSP is too much, ShieldingLEND is - //missing. Also, ShieldingLEND should only be there when G4LENDDATA is set. - const char * envG4LENDDATA_cstr = getenv("G4LENDDATA"); - std::string envG4LENDDATA(envG4LENDDATA_cstr?envG4LENDDATA_cstr:""); - bool allowLEND = !envG4LENDDATA.empty(); - - std::vector refpl; - bool sawShieldingLEND = false; - for (auto it = refpl_buggy.begin();it != refpl_buggy.end();++it) { - if (*it=="QGSP") - continue; - if (*it=="ShieldingLEND") { - sawShieldingLEND = true; - if (allowLEND) - refpl.push_back(*it); - } else { - refpl.push_back(*it); - } - } - if (allowLEND&&!sawShieldingLEND) - refpl.push_back("ShieldingLEND"); - - //combine hadronic and em parts (a large number admittedly): - for (auto it = refpl.begin();it != refpl.end();++it) { - for (auto itEM = refplEM.begin();itEM!= refplEM.end();++itEM) - v.push_back(*it+*itEM); - } -} diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory_py.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory_py.cc deleted file mode 100644 index 363af00..0000000 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/libsrc/PhysicsListFactory_py.cc +++ /dev/null @@ -1,17 +0,0 @@ -#include "Core/Python.hh" -#include "G4PhysicsLists/PhysicsListFactory.hh" -#include "G4Interfaces/PhysListProviderBase.hh" -#include "G4VUserPhysicsList.hh" - -G4VUserPhysicsList * PhysicsListFactory::attemptCreateCustomPhysicsList(const std::string& name) -{ - pyextra::ensurePyInit(); - auto mod = pyextra::pyimport("G4PhysicsLists"); - auto pyp = mod.attr("extractProvider")(name); - if (!pyp) - return nullptr; - G4Interfaces::PhysListProviderBase* p = pyp.cast(); - assert(p); - return p->construct(); -} - diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp__init/mod.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp__init/mod.cc index 006d46d..31145f4 100644 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp__init/mod.cc +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp__init/mod.cc @@ -1,20 +1,44 @@ #include "Core/Python.hh" -#include "G4PhysicsLists/PhysicsListFactory.hh" +#include "G4PhysicsLists/PhysListMgr.hh" namespace { - py::list pyPhysicsListFactory_getAllReferenceListNames() + + py::list pyPhysListMgr_getLists( bool with_descr, + bool include_g4ref, + bool include_custom ) { py::list l; - std::vector v; - PhysicsListFactory::getAllReferenceListNames(v); - for (auto it=v.begin();it!=v.end();++it) - l.append(*it); + if ( !include_g4ref && !include_custom ) + return l; + for ( auto& e : PhysListMgr::getAllLists() ) { + bool is_custom = !e.pkg_name.empty(); + if ( !include_g4ref && !is_custom ) + continue; + if ( !include_custom && is_custom ) + continue; + if ( with_descr ) { + std::string descr; + if ( is_custom ) { + descr = "List defined in package "; + descr += e.pkg_name; + } else { + descr = "Geant4 reference list"; + } + //FIXME: e.description could also be used?!? + l.append( py::make_tuple(py::str(e.name), + py::str(descr)) ); + } else { + l.append( e.name ); + } + } return l; } + + } PYTHON_MODULE( mod ) { - mod.def("getAllReferenceListNames",pyPhysicsListFactory_getAllReferenceListNames); + mod.def("_get_lists",&pyPhysListMgr_getLists); } diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp_g4physlist_Empty/mod.cc b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp_g4physlist_Empty/mod.cc deleted file mode 100644 index ec28801..0000000 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/pycpp_g4physlist_Empty/mod.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include "G4Interfaces/PhysicsListPyExport.hh" -#include "G4PhysicsLists/PhysicsListEmpty.hh" - -PYTHON_MODULE( mod ) -{ - PhysicsListPyExport::exportPhysList(mod); -} diff --git a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/python/__init__.py b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/python/__init__.py index e47757b..72b51a2 100644 --- a/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/python/__init__.py +++ b/src/simplebuild_dgcode/data/pkgs/G4/G4PhysicsLists/python/__init__.py @@ -1,70 +1,18 @@ #autogenerated file __doc__='python module for package G4PhysicsLists' -from . _init import * -custom_name_pattern = 'PL_%s' - -_custom = None -def _get_custom(): - global _custom - if _custom!=None: - return _custom - _custom={} - import pathlib - import sysconfig - import os - pymoddir = pathlib.Path( os.environ['SBLD_INSTALL_PREFIX'] ) / 'python' - assert pymoddir.is_dir() - pyext_suffix = sysconfig.get_config_var('EXT_SUFFIX') - for mod in pymoddir.glob('*/g4physlist_*%s'%pyext_suffix): - bn = mod.name[0:-len(pyext_suffix)] - packagename = mod.parent.name - physlistname = custom_name_pattern%bn[len('g4physlist_'):] - modulename = '%s.%s'%(packagename,bn) - if physlistname in _custom: - print("\n\nConfiguration ERROR: Multiple physics lists provided with the same name %s!!\n"%physlistname) - print(" They are located in the python modules:\n") - print(" %s"%modulename) - print(" %s\n"%_custom[physlistname][1]) - print(" One of them needs to be renamed!!\n\n") - import sys - sys.exit(1) - _custom[physlistname]=(packagename,modulename) - return _custom +from . _init import _get_lists as _cpp_get_lists def getAllListNames(include_description = False, include_base = True, include_custom = True): base=[] custom=[] if include_base: - base = getAllReferenceListNames() - if include_description: - base = [(b,'Geant4 reference list') for b in base] + base = _cpp_get_lists( include_description, True, False ) if include_custom: - if include_description: - custom = [(k,'List defined in package %s'%v[0]) for k,v in _get_custom().items()] - else: - custom = list(_get_custom().keys()) + custom = _cpp_get_lists( include_description, False, True ) return sorted(base)+sorted(custom) def listIsCustom(plname): - return plname in _get_custom() - -def extractProvider(plname): - if plname == 'empty': - #Accepted alias: - plname='PL_Empty' - _ = _get_custom().get(plname,None) - if _ is None and plname.startswith('ESS_'): - plnametry = 'PL_' + plname[len('ESS_'):] - print('G4Launcher:: WARNING Trying to map deprecated physics list' - f' name {plname} to {plnametry}') - _ = _get_custom().get(plnametry,None) - if _ is not None: - plname = plnametry - if _ is None: - raise RuntimeError('Unknown physics list: %s'%plname) - pkg,modname = _ - import importlib - return importlib.import_module(modname).create_provider(plname) + return plname.startswith('PL_') def printAvailableLists(): print ("Available physics lists (append +TS or +OPTICAL to inject thermal scattering / optical processes):")