Warning, /acts/docs/groups/detector_descr/runtime_geometry_modules.md is written in an unsupported language. File is not indexed.
0001 @defgroup geometry_module_loading Runtime geometry module loading
0002 @ingroup geometry
0003 @brief Loading tracking geometries from runtime shared libraries.
0004
0005 This topic documents the runtime geometry module loading entry points:
0006
0007 - @ref Acts::loadGeometryModule for modules that do not require user data.
0008 - @ref Acts::loadDD4hepGeometryModule for DD4hep-based modules.
0009
0010 Both loaders validate module ABI compatibility before constructing the
0011 @ref Acts::TrackingGeometry, and keep the shared library loaded for the
0012 lifetime of the returned geometry object.
0013
0014 ## Overview
0015
0016 Runtime geometry modules allow tracking geometries to be compiled as
0017 independent shared libraries (`.so`/`.dylib`) and loaded at runtime without
0018 recompiling the host application. This is useful when:
0019
0020 - The geometry changes frequently and recompiling the full framework is costly.
0021 - Different geometry variants need to be swapped at runtime.
0022 - The geometry is developed and distributed independently from the experiment
0023 framework.
0024
0025 @note Runtime geometry modules rely on `dlopen`/`dlsym` and are
0026 only supported on Unix-like systems (Linux, macOS).
0027
0028 ## The module ABI
0029
0030 Every geometry module must export a single C-linkage entry point:
0031
0032 ```c
0033 extern "C" const ActsGeometryModuleV1* acts_geometry_module_v1(void);
0034 ```
0035
0036 The returned `ActsGeometryModuleV1` struct (defined in
0037 `Acts/Geometry/GeometryModule.h`) carries four fields:
0038
0039 | Field | Type | Purpose |
0040 |--------------------|-------------------------------------------|----------------------------------------------------------|
0041 | `module_abi_tag` | `const char*` | Build-time ABI tag matched against the host library |
0042 | `user_data_type` | `const char*` | Type name of the extra context the module needs, or null |
0043 | `build` | `void* (*)(const void* user_data, const void* logger)` | Constructs the geometry; returns a heap-allocated `TrackingGeometry*` or null on failure |
0044 | `destroy` | `void (*)(void* handle)` | Deletes the geometry object returned by `build` |
0045
0046 You never fill this struct manually. Use the provided helper macros instead
0047 (see @ref geometry_module_writing "Writing a geometry module" below).
0048
0049 ## ABI compatibility
0050
0051 Each ACTS build is tagged with an opaque string (`ACTS_GEOMETRY_MODULE_ABI_TAG`)
0052 that encodes the host library ABI. Plain geometry modules use that tag as-is
0053 and are matched against the tag compiled into `ActsCore`.
0054
0055 DD4hep geometry modules use a DD4hep-specific extension of that tag:
0056
0057 ```text
0058 ${ACTS_GEOMETRY_MODULE_ABI_TAG}|dd4hep-${DD4hep_VERSION}
0059 ```
0060
0061 `loadDD4hepGeometryModule` matches `module_abi_tag` against the corresponding
0062 tag compiled into `Acts::PluginDD4hep`, so a DD4hep module must be built
0063 against both the same ACTS build and the same DD4hep version as the host
0064 plugin. Any mismatch causes an immediate `std::runtime_error`.
0065
0066 ## Loading a module
0067
0068 ### Plain module (no extra context)
0069
0070 Include `Acts/Geometry/GeometryModuleLoader.hpp`, then:
0071
0072 @snippet{trimleft} examples/geometry_module.cpp Load Plain Module
0073
0074 ### DD4hep module
0075
0076 Include `ActsPlugins/DD4hep/GeometryModuleLoader.hpp`, then:
0077
0078 @snippet{trimleft} examples/geometry_module.cpp Load DD4hep Module
0079
0080 The loader passes a pointer to `detector` through the opaque `void* user_data`
0081 argument. It validates that the module declares `user_data_type == "dd4hep::Detector"`;
0082 trying to load a plain module with `loadDD4hepGeometryModule` (or vice-versa)
0083 throws a descriptive `std::runtime_error`.
0084
0085 @anchor geometry_module_writing
0086 ## Writing a geometry module
0087
0088 ### Plain module
0089
0090 1. Create a source file with a build function:
0091
0092 @snippet{trimleft} examples/geometry_module_template.cpp Write Plain Module
0093
0094 2. Register it in CMake using `acts_add_geometry_module`:
0095
0096 @snippet{trimleft} CMakeLists.txt Plain Module CMake
0097
0098 The CMake helper creates a `SHARED` library target, links it against
0099 `Acts::Core`, and injects the `ACTS_GEOMETRY_MODULE_ABI_TAG` compile definition
0100 required by `ACTS_DEFINE_GEOMETRY_MODULE`.
0101
0102 ### DD4hep module
0103
0104 1. Create a source file with a build function that takes a `dd4hep::Detector`:
0105
0106 @snippet{trimleft} examples/geometry_module_template_dd4hep.cpp Write DD4hep Module
0107
0108 2. Register it in CMake using `acts_add_dd4hep_geometry_module`:
0109
0110 @snippet{trimleft} CMakeLists.txt DD4hep Module CMake
0111
0112 This links against `Acts::PluginDD4hep` instead of `Acts::Core` and sets
0113 `ACTS_GEOMETRY_MODULE_ABI_TAG` to the DD4hep-specific tag derived from the
0114 installed ACTS tag plus `DD4hep_VERSION`.
0115
0116 ## Lifetime management
0117
0118 The `std::shared_ptr<TrackingGeometry>` returned by both loaders uses a custom
0119 deleter that:
0120
0121 1. Calls `descriptor->destroy()` to delete the `TrackingGeometry` object via
0122 the module's own destructor (important for correct cross-boundary `delete`).
0123 2. Keeps a `shared_ptr<void>` to the `dlopen` handle alive, so the shared
0124 library is only unloaded **after** the geometry is destroyed — preventing
0125 use-after-unload of virtual dispatch tables or static data.
0126
0127 ## Error handling
0128
0129 All errors are reported as `std::runtime_error`. The loader performs several
0130 checks in order before invoking the build function:
0131
0132 - **File not found.** The path is checked with `std::filesystem::exists` before
0133 calling `dlopen`. This gives a clearer error than the linker error that
0134 `dlopen` would produce for a missing file.
0135
0136 - **`dlopen` failure.** If the operating system cannot load the shared library
0137 (e.g. missing transitive dependencies, wrong architecture), the error string
0138 from `dlerror` is included in the exception message.
0139
0140 - **Missing entry point.** The loader looks up the symbol
0141 `acts_geometry_module_v1` via `dlsym`. If it is absent the module was either
0142 not built with `ACTS_DEFINE_GEOMETRY_MODULE` / `ACTS_DEFINE_DD4HEP_GEOMETRY_MODULE`,
0143 or the symbol was stripped or hidden by the linker.
0144
0145 - **Null or incomplete descriptor.** The struct returned by the entry point must
0146 be non-null and have `module_abi_tag` set and both function-pointer fields (`build`, `destroy`)
0147 non-null. A null or partially initialized descriptor indicates a
0148 programming error in the module.
0149
0150 - **ABI tag mismatch.** `module_abi_tag` is compared against the tag baked into
0151 the host loader library at its own build time (`ActsCore` for plain modules,
0152 `Acts::PluginDD4hep` for DD4hep modules). A mismatch means the module and the
0153 host were built from different ACTS or DD4hep versions and cannot safely
0154 interoperate. Rebuild the module against the same ACTS installation and, for
0155 DD4hep modules, the same DD4hep version.
0156
0157 - **User-data type mismatch.** The `user_data_type` field in the descriptor is
0158 compared against the type the loader expects. If you call `loadGeometryModule`
0159 on a module that declares a `user_data_type` (i.e. it needs extra context such
0160 as a `dd4hep::Detector`), or call a typed loader (e.g. `loadDD4hepGeometryModule`)
0161 on a plain module, a `std::runtime_error` is thrown with a hint pointing to
0162 the correct loader to use.
0163
0164 - **`build` returns null.** The module's build function returned a null pointer,
0165 which means it failed to construct the geometry. Exceptions thrown inside
0166 `build` are caught by the helper and logged via `ACTS_ERROR` before the null
0167 is returned, so check the log for the underlying cause.