-
Notifications
You must be signed in to change notification settings - Fork 3
feat(apps): build individual subsystems from build_geometry #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
32a1dd0
274d1cb
6bddf29
135f739
293d852
3459d60
986a569
5695b98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,47 +1,95 @@ | ||||||||||||||||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||||||||||||||||||
| // Copyright (C) CERN for the benefit of the SHiP Collaboration | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // Build the SHiP GeoModel geometry and serialise it to a GeoModel SQLite (.db). | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // Usage: | ||||||||||||||||||||
| // build_geometry # full detector -> ship_geometry.db | ||||||||||||||||||||
| // build_geometry out.db # full detector -> out.db | ||||||||||||||||||||
| // build_geometry Calorimeter # just that subsystem -> Calorimeter.db | ||||||||||||||||||||
| // build_geometry Calorimeter c.db # just that subsystem -> c.db | ||||||||||||||||||||
| // build_geometry Target Magnet # assemble a selection -> ship_selection.db | ||||||||||||||||||||
| // build_geometry --list # list available subsystems | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // A token ending in ".db" is the output file; any other token names a | ||||||||||||||||||||
| // subsystem. With no subsystem named, the complete detector is built. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #include "SHiPGeometry/SHiPGeometry.h" | ||||||||||||||||||||
| #include "SHiPGeometry/SubsystemRegistry.h" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #include <GeoModelDBManager/GMDBManager.h> | ||||||||||||||||||||
| #include <GeoModelKernel/GeoPhysVol.h> | ||||||||||||||||||||
| #include <GeoModelWrite/WriteGeoModel.h> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #include <filesystem> | ||||||||||||||||||||
| #include <iostream> | ||||||||||||||||||||
| #include <string> | ||||||||||||||||||||
| #include <vector> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| int main(int argc, char* argv[]) { | ||||||||||||||||||||
| std::string outputFile = "ship_geometry.db"; | ||||||||||||||||||||
| if (argc > 1) { | ||||||||||||||||||||
| outputFile = argv[1]; | ||||||||||||||||||||
| std::string outputFile; | ||||||||||||||||||||
| std::vector<std::string> names; // requested subsystem(s) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| for (int i = 1; i < argc; ++i) { | ||||||||||||||||||||
| const std::string arg = argv[i]; | ||||||||||||||||||||
| if (arg == "--list") { | ||||||||||||||||||||
| for (const auto& n : SHiPGeometry::subsystemNames()) | ||||||||||||||||||||
| std::cout << n << "\n"; | ||||||||||||||||||||
| return 0; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| if (arg.ends_with(".db")) { | ||||||||||||||||||||
| outputFile = arg; | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| names.push_back(arg); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| std::cout << "Building SHiP geometry..." << std::endl; | ||||||||||||||||||||
| GeoVPhysVol* geometry = nullptr; | ||||||||||||||||||||
| std::string label; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| SHiPGeometry::SHiPGeometryBuilder builder; | ||||||||||||||||||||
| GeoPhysVol* world = builder.build(); | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| if (names.empty()) { | ||||||||||||||||||||
| // Default: the complete detector (unchanged behaviour). | ||||||||||||||||||||
| SHiPGeometry::SHiPGeometryBuilder builder; | ||||||||||||||||||||
| geometry = builder.build(); | ||||||||||||||||||||
| label = "full SHiP geometry"; | ||||||||||||||||||||
| if (outputFile.empty()) | ||||||||||||||||||||
| outputFile = "ship_geometry.db"; | ||||||||||||||||||||
| } else if (names.size() == 1) { | ||||||||||||||||||||
| // A single subsystem, on its own (local frame). | ||||||||||||||||||||
| geometry = SHiPGeometry::buildSubsystem(names[0]); | ||||||||||||||||||||
| label = names[0]; | ||||||||||||||||||||
| if (outputFile.empty()) | ||||||||||||||||||||
| outputFile = names[0] + ".db"; | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| // Several subsystems: assemble them into the world at their declared | ||||||||||||||||||||
| // placements (the world/cavern is always included). | ||||||||||||||||||||
| geometry = SHiPGeometry::assembleGeometry(names); | ||||||||||||||||||||
| label = "selection (" + std::to_string(names.size()) + " subsystems)"; | ||||||||||||||||||||
| if (outputFile.empty()) | ||||||||||||||||||||
| outputFile = "ship_selection.db"; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } catch (const std::exception& e) { | ||||||||||||||||||||
| std::cerr << "Error: " << e.what() << "\nAvailable subsystems:\n"; | ||||||||||||||||||||
| for (const auto& n : SHiPGeometry::subsystemNames()) | ||||||||||||||||||||
| std::cerr << " " << n << "\n"; | ||||||||||||||||||||
| return 1; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (!world) { | ||||||||||||||||||||
| std::cerr << "Error: Geometry not yet implemented" << std::endl; | ||||||||||||||||||||
| if (!geometry) { | ||||||||||||||||||||
| std::cerr << "Error: geometry is null (not yet implemented?)." << std::endl; | ||||||||||||||||||||
| return 1; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Remove existing output file | ||||||||||||||||||||
| if (std::filesystem::exists(outputFile)) { | ||||||||||||||||||||
| std::filesystem::remove(outputFile); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
84
to
86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
The throwing overload is used; a locked or permission-denied output file would crash the tool instead of producing a clean error. The preceding 🛡️ Proposed fix- if (std::filesystem::exists(outputFile)) {
- std::filesystem::remove(outputFile);
- }
+ std::error_code ec;
+ std::filesystem::remove(outputFile, ec);
+ if (ec) {
+ std::cerr << "Warning: could not remove existing " << outputFile << ": " << ec.message()
+ << std::endl;
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| std::cout << "Writing geometry to " << outputFile << std::endl; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| std::cout << "Writing " << label << " to " << outputFile << std::endl; | ||||||||||||||||||||
| GMDBManager db(outputFile); | ||||||||||||||||||||
| GeoModelIO::WriteGeoModel writer(db); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Traverse the geometry tree | ||||||||||||||||||||
| world->exec(&writer); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Save to database | ||||||||||||||||||||
| geometry->exec(&writer); | ||||||||||||||||||||
| writer.saveToDB(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| std::cout << "Done." << std::endl; | ||||||||||||||||||||
| return 0; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
| // Copyright (C) CERN for the benefit of the SHiP Collaboration | ||
|
|
||
| #pragma once | ||
|
|
||
| namespace SHiPGeometry { | ||
|
|
||
| /** | ||
| * @brief A subsystem's self-description: everything the assembler needs. | ||
| * | ||
| * This is the "required input to the geometry builder" that each subsystem | ||
| * yields about itself. It is a plain data type with no GeoModel or standard- | ||
| * library dependency, so it is cheap to include from the factory headers | ||
| * (which only need it as the return type of `descriptor()`). The registry | ||
| * machinery that consumes it lives in SubsystemRegistry.h, included by the | ||
| * factory implementations. | ||
| * | ||
| * Translations are in millimetres from the world origin. | ||
| */ | ||
| struct SubsystemDescriptor { | ||
| const char* name; ///< registry key / CLI name, e.g. "Calorimeter" | ||
| const char* node; ///< GeoNameTag, e.g. "/SHiP/calorimeter" | ||
| int id; ///< GeoIdentifierTag | ||
| double x_mm; ///< placement translation X (mm) | ||
| double y_mm; ///< placement translation Y (mm) | ||
| double z_mm; ///< placement translation Z, beam direction (mm) | ||
| bool isWorld = false; ///< true for the mother/world volume (the cavern) | ||
| }; | ||
|
|
||
| } // namespace SHiPGeometry |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
| // Copyright (C) CERN for the benefit of the SHiP Collaboration | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "SHiPGeometry/SubsystemDescriptor.h" | ||
|
|
||
| #include <GeoModelKernel/GeoPhysVol.h> // complete GeoPhysVol/GeoVPhysVol for the macro's upcast | ||
|
|
||
| #include <cstdio> | ||
| #include <cstdlib> | ||
| #include <functional> | ||
| #include <map> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <vector> | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| namespace SHiPGeometry { | ||
|
|
||
| class SHiPMaterials; | ||
|
|
||
| /// A registered subsystem: its descriptor plus how to build it (local frame). | ||
| struct SubsystemInfo { | ||
| SubsystemDescriptor desc; | ||
| std::function<GeoVPhysVol*(SHiPMaterials&)> build; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief The global subsystem registry. | ||
| * | ||
| * A Meyers singleton (function-local static in an inline function) so it is | ||
| * a single shared instance across all translation units and is guaranteed to | ||
| * exist before any static registration runs. No file names any subsystem; | ||
| * subsystems add themselves via REGISTER_SUBSYSTEM. | ||
| */ | ||
| inline std::map<std::string, SubsystemInfo>& registry() { | ||
| static std::map<std::string, SubsystemInfo> instance; | ||
| return instance; | ||
| } | ||
|
|
||
| /// Add a subsystem to the registry. Returns true (usable as a static | ||
| /// initialiser). A duplicate name is a programming error (two subsystems | ||
| /// declaring the same descriptor name): it is reported and aborts, rather | ||
| /// than being silently dropped by emplace(). Runs at static-init, so this | ||
| /// diagnoses to stderr and aborts instead of throwing. | ||
| inline bool registerSubsystem(const SubsystemDescriptor& desc, | ||
| std::function<GeoVPhysVol*(SHiPMaterials&)> build) { | ||
| const auto result = registry().emplace(desc.name, SubsystemInfo{desc, std::move(build)}); | ||
| if (!result.second) { | ||
| std::fprintf(stderr, | ||
| "SHiPGeometry: duplicate subsystem name '%s' registered; " | ||
| "each subsystem's descriptor() must return a unique name.\n", | ||
| desc.name); | ||
| std::abort(); | ||
| } | ||
| return true; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| // ── Generic consumers — these name no subsystem ───────────────────────────── | ||
|
|
||
| /// Assemble the world plus a selection of subsystems (empty selection = all), | ||
| /// each placed at its own declared position. Unknown names throw | ||
| /// std::runtime_error. Returns the world volume. | ||
| GeoPhysVol* assembleGeometry(const std::vector<std::string>& only = {}); | ||
|
|
||
| /// Build a single subsystem on its own, in its local frame (no world, no | ||
| /// placement). Throws std::runtime_error if the name is not registered. | ||
| GeoVPhysVol* buildSubsystem(const std::string& name); | ||
|
|
||
| /// The names of all registered subsystems (including the world), sorted. | ||
| std::vector<std::string> subsystemNames(); | ||
|
|
||
| } // namespace SHiPGeometry | ||
|
|
||
| /** | ||
| * @brief Register a subsystem factory with the global registry. | ||
| * | ||
| * Placed once in each subsystem's own .cpp (inside namespace SHiPGeometry). | ||
| * The factory must expose `static SubsystemDescriptor descriptor()` and be | ||
| * constructible from `SHiPMaterials&` with a `build()` returning a volume. | ||
| * | ||
| * NOTE: nothing references this registration, so the subsystem library would | ||
| * otherwise be dropped from a consumer's DT_NEEDED by the toolchain's default | ||
| * --as-needed and the initialiser would never run. src/CMakeLists.txt applies | ||
| * -Wl,--no-as-needed as an INTERFACE link option on SHiPGeometry so the flag | ||
| * lands on each executable's link line. Do not remove it. | ||
| */ | ||
| #define REGISTER_SUBSYSTEM(FACTORY) \ | ||
| namespace { \ | ||
| const bool FACTORY##_registered = ::SHiPGeometry::registerSubsystem( \ | ||
| FACTORY::descriptor(), [](::SHiPGeometry::SHiPMaterials& materials) -> ::GeoVPhysVol* { \ | ||
| return FACTORY(materials).build(); \ | ||
| }); \ | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a language identifier to the fenced code block.
markdownlint flags this block (MD040) since it has no language specified.
📝 Proposed fix
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 170-170: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Source: Linters/SAST tools
✅ Addressed in commits 40b4a66 to b7d33d4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not actually resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Length of output: 816
🏁 Script executed:
Length of output: 2577
@olantwin, you’re right — it is not resolved.README.mdstill has an unlabeled opening fence at Line 180 for thegeometry/structure tree, so it still triggers MD040.The minimal fix remains: