From e034ada099132f857866949ec9ce8eadf1ff2251 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Mon, 12 Feb 2024 08:48:05 -0500 Subject: [PATCH] Add initial implementation (#2) * Add clang-format and gitignore * Move celeritas load to a subdirectory * Set up default celeritas options * Add implementation * Add targets for unit test debugging * Implement test * Add CI * Use incoming celeritas PR * Fix GHA branch target * Update celeritas URL --- .clang-format | 161 +++++++ .github/workflows/build-full.yml | 65 +++ .github/workflows/pr.yml | 35 ++ .github/workflows/push.yml | 27 ++ .gitignore | 18 + CMakeLists.txt | 71 ++- CMakePresets.json | 67 +++ cmake/FindGeant4.cmake | 58 +++ cmake/FindVecGeom.cmake | 59 +++ external/CMakeLists.txt | 48 ++ scripts/cmake-presets/ci-ubuntu-cuda.json | 95 ++++ src/CMakeLists.txt | 39 ++ src/G4VG.cc | 58 +++ src/G4VG.hh | 69 +++ test/CMakeLists.txt | 24 + test/G4VG.test.cc | 166 +++++++ test/data/solids.gdml | 521 ++++++++++++++++++++++ test/g4vg_test_config.h.in | 14 + 18 files changed, 1585 insertions(+), 10 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/build-full.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/push.yml create mode 100644 CMakePresets.json create mode 100644 cmake/FindGeant4.cmake create mode 100644 cmake/FindVecGeom.cmake create mode 100644 external/CMakeLists.txt create mode 100644 scripts/cmake-presets/ci-ubuntu-cuda.json create mode 100644 src/CMakeLists.txt create mode 100644 src/G4VG.cc create mode 100644 src/G4VG.hh create mode 100644 test/CMakeLists.txt create mode 100644 test/G4VG.test.cc create mode 100644 test/data/solids.gdml create mode 100644 test/g4vg_test_config.h.in diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f9695d9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,161 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 79 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +# Category 0: main include +# Category 1: system includes +# Category 2: celeritas absolute includes +# Category 5: relative includes +# Category 7: test includes +IncludeCategories: + - Regex: '^<[^/.]+>$' + Priority: 1 + SortPriority: 1 + - Regex: '^<.+>$' + Priority: 1 + SortPriority: 2 + - Regex: '^"celeritas_[a-z_]+\.h"' + Priority: 2 + SortPriority: 3 + - Regex: '^"corecel/device_runtime_api\.h"$' + Priority: 2 + SortPriority: 3 + - Regex: '^"corecel/' + Priority: 2 + SortPriority: 5 + CaseSensitive: true + - Regex: '^"orange/' + Priority: 2 + SortPriority: 6 + CaseSensitive: true + - Regex: '^"celeritas/' + Priority: 2 + SortPriority: 7 + CaseSensitive: true + - Regex: '^"accel/' + Priority: 2 + SortPriority: 8 + CaseSensitive: true + - Regex: '^"[^/]+"' + Priority: 5 + SortPriority: 9 + - Regex: '^"detail/"' + Priority: 5 + SortPriority: 10 + - Regex: '"(^gtest/|TestBase|\.test\.hh|celeritas_test\.hh)' + Priority: 7 + CaseSensitive: true + SortPriority: 11 + - Regex: '.*' + Priority: 5 + SortPriority: 9 +IncludeIsMainRegex: '(\.[^.]+)?$' +IncludeIsMainSourceRegex: '(\.cu|\.t\.hh)$' # Allow CU/template files as main +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 20 +PenaltyBreakBeforeFirstCallParameter: 5 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 1 +PenaltyBreakString: 25 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 25 +PenaltyReturnTypeOnItsOwnLine: 10 +PointerAlignment: Left +QualifierAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... +# vim: set ft=yaml ts=2 sw=2 : diff --git a/.github/workflows/build-full.yml b/.github/workflows/build-full.yml new file mode 100644 index 0000000..8b6fb39 --- /dev/null +++ b/.github/workflows/build-full.yml @@ -0,0 +1,65 @@ +name: build-full +on: + workflow_dispatch: + workflow_call: + +concurrency: + group: build-${{github.ref}}-${{github.event.pull_request.number || github.run_number}}-${{github.workflow}} + cancel-in-progress: true + +jobs: + # TODO: this currently includes non-GPU builds as well + # and won't actually run on device + gpu: + name: gpu + strategy: + fail-fast: false + matrix: + special: [null] + geometry: ['vecgeom'] + buildtype: ['debug', 'ndebug'] + image: ['ubuntu-cuda'] + env: + CELER_DISABLE_DEVICE: 1 # IMPORTANT + CMAKE_PRESET: >- + ${{matrix.buildtype}}-${{matrix.geometry}}${{matrix.special && '-' || ''}}${{matrix.special}} + runs-on: ubuntu-latest + container: + image: >- + docker.io/celeritas/${{ + matrix.image == 'ubuntu-cuda' && 'ci-jammy-cuda11:2023-08-02' + || null + }} + # See https://github.com/actions/checkout/issues/956 + options: --user root + steps: + - name: Set up environment + run: | + . /etc/profile + echo "/opt/view/bin" >> $GITHUB_PATH + echo "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" >> $GITHUB_ENV + # NOTE: checkout must occur *after* setting up environment for git tags to work + # NOTE: checkout v4 fails: https://github.com/actions/checkout/issues/1487 + # NOTE: depth must be enough to include the previous tag + - name: Check out + uses: actions/checkout@v3 + - name: Configure + run: | + git config --global --add safe.directory ${PWD} + ln -fs scripts/cmake-presets/ci-${{matrix.image}}.json CMakeUserPresets.json + cmake --preset=${CMAKE_PRESET} + - name: Build + working-directory: build + run: | + ninja + - name: Test + working-directory: build + run: | + ctest --parallel 2 --timeout 180 --output-on-failure \ + --test-output-size-passed=65536 --test-output-size-failed=1048576 + - name: Install + working-directory: build + run: | + cmake --install . + +# vim: set nowrap tw=100: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..388ecc0 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,35 @@ +name: pr +run-name: >- + ${{github.event.pull_request.title}} + (#${{github.event.number}}) + +on: + pull_request: + branches: + - main + paths-ignore: + - '**.rst' + - '**.md' + - 'scripts/dev' + +concurrency: + group: pr-${{github.ref}}-${{github.event.number}}-${{github.workflow}} + cancel-in-progress: true + +jobs: + build: + uses: ./.github/workflows/build-full.yml + + # Specifying a dependent job allows us to select a single "requires" check in the project GitHub settings + all: + if: ${{ always() }} + needs: + - build + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + +# vim: set nowrap tw=100: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..1a71175 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,27 @@ +name: push +# TODO: improve run name using commit title (`message` includes details as well) +# run-name: >- +# ${{github.ref_name}}: +# ${{github.event.head_commit.message}} +# (${{github.event.pusher.name}}) + +on: + push: + branches: + - main + +concurrency: + group: push-${{github.ref}}-${{github.run_number}}-${{github.workflow}} + cancel-in-progress: true + +jobs: + build-full: + uses: ./.github/workflows/build-full.yml + all: + needs: [build-full] + runs-on: ubuntu-latest + steps: + - name: Success + run: "true" + +# vim: set nowrap tw=100: diff --git a/.gitignore b/.gitignore index 259148f..84c6635 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,21 @@ *.exe *.out *.app + +# Misc +*.cache +*.pyc +*.swp +*~ +.DS_Store +.nfs* +.vscode +/build* +/install* +/spack-build* +/CMakeUserPresets.json +Testing +compile_commands.json +cmake_install.cmake +/CMakeCache.txt +/CMakeFiles diff --git a/CMakeLists.txt b/CMakeLists.txt index 736483a..0aac8db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,66 @@ cmake_minimum_required(VERSION 3.14...3.28) project(G4VG VERSION 0.1.0 LANGUAGES CXX) -include(FetchContent) -FetchContent_Declare( - celeritas - # Current tip of celeritas develop - adjust as needed - URL https://github.com/celeritas-project/celeritas/archive/5742b0c924a67bf02ec0ce9fc85719a70f7857e0.zip -) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +#----------------------------------------------------------------------------# +# Utilities + +include(GNUInstallDirs) + +macro(g4vg_set_default name value) + if(NOT DEFINED ${name}) + message(VERBOSE "G4VG: set default ${name}=${value}") + set(${name} "${value}") + endif() +endmacro() -# Set/force any Celeritas CMake args here before making it available, e.g. -set(CELERITAS_USE_MPI OFF CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(celeritas) +#----------------------------------------------------------------------------# +# Options + +option(G4VG_BUILD_TESTS "Build G4VG unit tests" OFF) +g4vg_set_default(BUILD_TESTING ${G4VG_BUILD_TESTS}) + +g4vg_set_default(CMAKE_CXX_EXTENSIONS OFF) + +#----------------------------------------------------------------------------# +# Add code -# Use any celeritas targets as normal (or CMake modules) +add_subdirectory(external) +add_subdirectory(src) + +#----------------------------------------------------------------------------# +# Add tests + +if(BUILD_TESTING) + include(CTest) +endif() +if(G4VG_BUILD_TESTS) + if(NOT GTest_FOUND) + find_package(GTest 1.10 REQUIRED) + endif() + if(NOT VecGeom_FOUND) + # Note: using the same version as celeritas to silence cmake config + # messages + find_package(VecGeom 1.2.4 REQUIRED) + endif() + if(NOT Geant4_FOUND) + find_package(Geant4 REQUIRED) + endif() + + add_subdirectory(test) +endif() + +#----------------------------------------------------------------------------# +# Export CMake for installation downstream + +set(G4VG_INSTALL_CMAKECONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/G4VG") + +# Install 'G4VGTargets.cmake', included by G4VGConfig.cmake, which +# references the targets we install. +install(EXPORT g4vg-targets + FILE G4VGTargets.cmake + NAMESPACE G4VG:: + DESTINATION "${G4VG_INSTALL_CMAKECONFIGDIR}" + COMPONENT development +) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..fc89813 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,67 @@ +{ + "version": 3, + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, + "configurePresets": [ + { + "name": "default", + "displayName": "Automatic options", + "description": "Dependencies are enabled based on environment probing", + "binaryDir": "${sourceDir}/build-${presetName}", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install-${presetName}" + } + }, + { + "name": ".cuda-volta", + "hidden": true, + "description": "Options to enable CUDA 11 on Volta architecture", + "cacheVariables": { + "CELERITAS_USE_CUDA": {"type": "BOOL", "value": "ON"}, + "CMAKE_CUDA_ARCHITECTURES": {"type": "STRING", "value": "70"} + } + }, + { + "name": ".ndebug", + "hidden": true, + "description": "Build with optimizations and without debug assertions", + "cacheVariables": { + "CMAKE_BUILD_TYPE": {"type": "STRING", "value": "Release"}, + "CMAKE_CUDA_FLAGS_RELEASE": "-O3 -DNDEBUG" + } + }, + { + "name": ".reldeb", + "hidden": true, + "description": "Enable debug with basic optimizations", + "cacheVariables": { + "BUILD_SHARED_LIBS":{"type": "BOOL", "value": "ON"}, + "CMAKE_BUILD_TYPE": {"type": "STRING", "value": "RelWithDebInfo"} + } + }, + { + "name": ".debug", + "hidden": true, + "description": "Enable debug mode", + "cacheVariables": { + "BUILD_SHARED_LIBS":{"type": "BOOL", "value": "ON"}, + "CMAKE_BUILD_TYPE": {"type": "STRING", "value": "Debug"} + } + } + ], + "buildPresets": [ + { + "name": "default", + "jobs": 0, + "configurePreset": "default" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default", + "output": {"outputOnFailure": true}, + "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 8} + } + ] +} diff --git a/cmake/FindGeant4.cmake b/cmake/FindGeant4.cmake new file mode 100644 index 0000000..69c220b --- /dev/null +++ b/cmake/FindGeant4.cmake @@ -0,0 +1,58 @@ +#----------------------------------*-CMake-*----------------------------------# +# Copyright 2022-2024 UT-Battelle, LLC and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +#[=======================================================================[.rst: + +FindGeant4 +---------- + +Find the Geant4 HEP library. + +#]=======================================================================] + +# Save and restore global settings changed by Geant's find script +get_directory_property(_include_dirs INCLUDE_DIRECTORIES) + +find_package(Geant4 QUIET CONFIG) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Geant4 CONFIG_MODE) + +if(Geant4_FOUND AND Geant4_VERSION VERSION_GREATER_EQUAL 11 AND CELERITAS_USE_CUDA) + foreach(_tgt Geant4::G4global Geant4::G4global-static) + if(TARGET ${_tgt}) + target_compile_features(${_tgt} INTERFACE cuda_std_17) + endif() + endforeach() +endif() + +if(Geant4_VERSION VERSION_LESS 10.6) + # Version 10.5 and older have some problems. + + # Move definitions from CXX flags to geant4 definitions + string(REGEX MATCHALL "-D[a-zA-Z0-9_]+" _defs "${Geant4_CXX_FLAGS}") + list(APPEND Geant4_DEFINITIONS ${_defs}) + unset(defs) + + # Make a fake target with includes and definitions + set(_tgt Geant4_headers) + if(NOT TARGET "${_tgt}") + add_library(${_tgt} INTERFACE) + add_library(celeritas::${_tgt} ALIAS ${_tgt}) + target_include_directories(${_tgt} INTERFACE ${Geant4_INCLUDE_DIRS}) + target_compile_definitions(${_tgt} INTERFACE ${Geant4_DEFINITIONS}) + install(TARGETS ${_tgt} EXPORT celeritas-targets) + endif() + # Add the fake target to the list of geant4 libraries + list(APPEND Geant4_LIBRARIES ${_tgt}) + unset(_tgt) +endif() +if(Geant4_FOUND) + # Geant4 calls `include_directories` for CLHEP :( which is not what we want! + # Save and restore include directories around the call -- even though as a + # standalone project Celeritas will never have directory-level includes + set_directory_properties(PROPERTIES INCLUDE_DIRECTORIES "${_include_dirs}") +endif() +unset(_include_dirs) + +#-----------------------------------------------------------------------------# \ No newline at end of file diff --git a/cmake/FindVecGeom.cmake b/cmake/FindVecGeom.cmake new file mode 100644 index 0000000..24778de --- /dev/null +++ b/cmake/FindVecGeom.cmake @@ -0,0 +1,59 @@ +#----------------------------------*-CMake-*----------------------------------# +# Copyright 2022-2024 UT-Battelle, LLC and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +#[=======================================================================[.rst: + +FindVecGeom +----------- + +Find the VecGeom library and set up library linking and flags for use with +Celeritas. + +#]=======================================================================] + +find_package(VecGeom QUIET CONFIG) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(VecGeom CONFIG_MODE) + +if(VecGeom_FOUND AND TARGET VecGeom::vecgeomcuda) + get_target_property(_vecgeom_lib_type VecGeom::vecgeom TYPE) + if (_vecgeom_lib_type STREQUAL "STATIC_LIBRARY") + set(_vecgeom_cuda_runtime "Static") + else() + set(_vecgeom_cuda_runtime "Shared") + endif() + set_target_properties(VecGeom::vecgeom PROPERTIES + CELERITAS_CUDA_STATIC_LIBRARY VecGeom::vecgeomcuda_static + CELERITAS_CUDA_MIDDLE_LIBRARY VecGeom::vecgeomcuda + CELERITAS_CUDA_FINAL_LIBRARY VecGeom::vecgeomcuda + ) + set_target_properties(VecGeom::vecgeomcuda PROPERTIES + CELERITAS_CUDA_LIBRARY_TYPE Shared + #CUDA_RUNTIME_LIBRARY ${_vecgeom_cuda_runtime} + ) + set_target_properties(VecGeom::vecgeomcuda_static PROPERTIES + CELERITAS_CUDA_LIBRARY_TYPE Static + ) + # Suppress warnings from virtual function calls in RDC + foreach(_lib VecGeom::vecgeomcuda VecGeom::vecgeomcuda_static) + target_compile_options(${_lib} + INTERFACE "$<$:SHELL: -Xnvlink --suppress-stack-size-warning>" + ) + target_link_options(${_lib} + INTERFACE "$" + ) + endforeach() + + # Inform celeritas_add_library code + foreach(_lib VecGeom::vecgeom VecGeom::vecgeomcuda + VecGeom::vecgeomcuda_static) + set_target_properties(${_lib} PROPERTIES + CELERITAS_CUDA_STATIC_LIBRARY VecGeom::vecgeomcuda_static + CELERITAS_CUDA_MIDDLE_LIBRARY VecGeom::vecgeomcuda + CELERITAS_CUDA_FINAL_LIBRARY VecGeom::vecgeomcuda + ) + endforeach() +endif() + +#-----------------------------------------------------------------------------# diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..5a8c042 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,48 @@ +#----------------------------------*-CMake-*----------------------------------# +# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +#-----------------------------------------------------------------------------# + +include(FetchContent) + +# Load Celeritas +FetchContent_Declare( + Celeritas + EXCLUDE_FROM_ALL + URL https://github.com/celeritas-project/celeritas/archive/052ac4cd74abc0a6805f2a4168b4683bb589bb59.zip +) + +# $ git "describe" "--tags" "--match" "v*" 052ac4cd7; format as below +set(Celeritas_GIT_DESCRIBE "0.5.0" "-dev.20" "052ac4cd7") + +# Minimally build Celeritas +g4vg_set_default(CELERITAS_BUILD_DEMOS OFF) +g4vg_set_default(CELERITAS_BUILD_DOCS OFF) +g4vg_set_default(CELERITAS_BUILD_TESTS OFF) + +g4vg_set_default(CELERITAS_USE_CUDA OFF) +g4vg_set_default(CELERITAS_USE_Geant4 ON) +g4vg_set_default(CELERITAS_USE_HIP OFF) +g4vg_set_default(CELERITAS_USE_HepMC3 OFF) +g4vg_set_default(CELERITAS_USE_JSON OFF) +g4vg_set_default(CELERITAS_USE_MPI OFF) +g4vg_set_default(CELERITAS_USE_OpenMP OFF) +g4vg_set_default(CELERITAS_USE_Python OFF) +g4vg_set_default(CELERITAS_USE_ROOT OFF) +g4vg_set_default(CELERITAS_USE_SWIG OFF) +g4vg_set_default(CELERITAS_USE_VecGeom ON) + +# Use VecGeom, mm, and double precision +g4vg_set_default(CELERITAS_CORE_GEO VecGeom) +g4vg_set_default(CELERITAS_UNITS CLHEP) +g4vg_set_default(CELERITAS_REAL_TYPE double) + +# Build static libraries for celeritas +g4vg_set_default(BUILD_SHARED_LIBS OFF) +g4vg_set_default(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Load Celeritas +FetchContent_MakeAvailable(Celeritas) + +#-----------------------------------------------------------------------------# diff --git a/scripts/cmake-presets/ci-ubuntu-cuda.json b/scripts/cmake-presets/ci-ubuntu-cuda.json new file mode 100644 index 0000000..82ada84 --- /dev/null +++ b/scripts/cmake-presets/ci-ubuntu-cuda.json @@ -0,0 +1,95 @@ +{ + "version": 3, + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, + "configurePresets": [ + { + "name": "base", + "displayName": "Ubuntu/nvidia-docker default options for GCC", + "inherits": [".cuda-volta", "default"], + "binaryDir": "${sourceDir}/build", + "generator": "Ninja", + "cacheVariables": { + "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "ON"}, + "G4VG_BUILD_TESTS": {"type": "BOOL", "value": "ON"}, + "CMAKE_CXX_STANDARD": "17", + "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, + "CMAKE_CXX_FLAGS": "-Wall -Wextra -pedantic -Werror -Wno-error=deprecated-declarations", + "CMAKE_CUDA_FLAGS": "-Werror all-warnings", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install" + } + }, + { + "name": ".mpi", + "hidden": true, + "description": "Options to enable MPI with Docker", + "cacheVariables": { + "CELERITAS_USE_MPI": {"type": "BOOL", "value": "ON"}, + "MPI_CXX_LINK_FLAGS": "-pthread", + "MPIEXEC_PREFLAGS": "--allow-run-as-root" + } + }, + { + "name": ".vecgeom", + "hidden": true, + "description": "Options to enable VecGeom on Ubuntu", + "cacheVariables": { + } + }, + { + "name": "debug-vecgeom", + "description": "Build tests in debug with vecgeom", + "inherits": [".vecgeom", "base"], + "cacheVariables": { + "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "OFF"} + } + }, + { + "name": "reldeb-vecgeom", + "description": "Build with RelWithDebInfo, assertions, and VecGeom", + "inherits": [".reldeb", ".vecgeom", "base"] + }, + { + "name": "ndebug-vecgeom", + "description": "Build release with vecgeom for testing *only* demos", + "inherits": [".ndebug", ".vecgeom", "base"], + "cacheVariables": { + } + } + ], + "buildPresets": [ + { + "name": "base", + "configurePreset": "base", + "nativeToolOptions": ["-k0"], + "verbose": true, + "jobs": 16 + }, + {"name": "debug-orange" , "configurePreset": "debug-orange" , "inherits": "base"}, + {"name": "ndebug-orange", "configurePreset": "ndebug-orange", "inherits": "base", "targets": ["all", "install"]}, + {"name": "reldeb-vecgeom" , "configurePreset": "reldeb-vecgeom" , "inherits": "base", "targets": ["all", "install"]}, + {"name": "debug-vecgeom" , "configurePreset": "debug-vecgeom" , "inherits": "base", "jobs": 8}, + {"name": "ndebug-vecgeom" , "configurePreset": "ndebug-vecgeom" , "inherits": "base", "jobs": 8, "targets": ["app/all", "install"]} + ], + "testPresets": [ + { + "name": "base", + "configurePreset": "base", + "execution": { + "noTestsAction": "error", + "stopOnFailure": false, + "jobs": 16, + "timeout": 180 + }, + "output": { + "maxFailedTestOutputSize": 1048576, + "maxPassedTestOutputSize": 65536, + "outputOnFailure": true + } + }, + {"name": "debug-orange" , "configurePreset": "debug-orange" , "inherits": "base"}, + {"name": "ndebug-orange", "configurePreset": "ndebug-orange", "inherits": "base"}, + {"name": "reldeb-vecgeom" , "configurePreset": "reldeb-vecgeom" , "inherits": "base"}, + {"name": "debug-vecgeom" , "configurePreset": "debug-vecgeom" , "inherits": "base"}, + {"name": "ndebug-vecgeom" , "configurePreset": "ndebug-vecgeom" , "inherits": "base"} + ] +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..29c1b32 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,39 @@ +#----------------------------------*-CMake-*----------------------------------# +# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +#-----------------------------------------------------------------------------# + +#-----------------------------------------------------------------------------# +# Add the library +celeritas_rdc_add_library(g4vg SHARED + G4VG.cc +) +celeritas_target_link_libraries(g4vg + PRIVATE Celeritas::geocel +) +celeritas_target_include_directories(g4vg + PUBLIC + "$" + "$" +) + +# Alias the library for downstream code +add_library(G4VG::g4vg ALIAS g4vg) + +# Install all targets to lib/ +install(TARGETS g4vg + EXPORT g4vg-targets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + COMPONENT runtime +) + +# C++ source headers +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT development + FILES_MATCHING REGEX ".*\\.hh?$" +) + +#-----------------------------------------------------------------------------# diff --git a/src/G4VG.cc b/src/G4VG.cc new file mode 100644 index 0000000..828ece8 --- /dev/null +++ b/src/G4VG.cc @@ -0,0 +1,58 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file G4VG.cc +//---------------------------------------------------------------------------// +#include "G4VG.hh" + +#include + +namespace g4vg +{ +//---------------------------------------------------------------------------// +/*! + * Convert a Geant4 geometry to a VecGeom geometry. + * + * Return the new world volume and a mapping of Geant4 logical volumes to + * VecGeom-based volume IDs. + */ +Converted convert(G4VPhysicalVolume const* world) +{ + return convert(world, {}); +} + +//---------------------------------------------------------------------------// +/*! + * Convert with custom options. + */ +Converted convert(G4VPhysicalVolume const* world, Options options) +{ + using Converter = ::celeritas::g4vg::Converter; + + // Construct converter + Converter convert{[&options] { + Converter::Options geocel_opts; + geocel_opts.verbose = options.verbose; + geocel_opts.compare_volumes = options.compare_volumes; + return geocel_opts; + }()}; + + // Convert + auto geocel_result = convert(world); + + // Remap output to remove volume IDs + Converted converted; + converted.world = geocel_result.world; + converted.volumes.reserve(geocel_result.volumes.size()); + for (auto&& [lv, vid] : geocel_result.volumes) + { + converted.volumes.insert({lv, vid.unchecked_get()}); + } + + return converted; +} + +//---------------------------------------------------------------------------// +} // namespace g4vg diff --git a/src/G4VG.hh b/src/G4VG.hh new file mode 100644 index 0000000..1a8d2a2 --- /dev/null +++ b/src/G4VG.hh @@ -0,0 +1,69 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file G4VG.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +//---------------------------------------------------------------------------// +// FORWARD DECLARATIONS +//---------------------------------------------------------------------------// +class G4LogicalVolume; +class G4VPhysicalVolume; + +namespace vecgeom +{ +inline namespace cxx +{ +class VPlacedVolume; +} // namespace cxx +} // namespace vecgeom +//---------------------------------------------------------------------------// + +namespace g4vg +{ +//---------------------------------------------------------------------------// +/*! + * Construction options to pass to the converter. + */ +struct Options +{ + //! Print extra messages for debugging + bool verbose{false}; + + //! Perform conversion checks + bool compare_volumes{false}; + + //! TODO: allow client to use a different unit system (default: mm = 1) + static constexpr double scale = 1; +}; + +//---------------------------------------------------------------------------// +/*! + * Result from converting from Geant4 to VecGeom. + */ +struct Converted +{ + using VGPlacedVolume = vecgeom::VPlacedVolume; + using MapLvVolId = std::unordered_map; + + //! World pointer (host) corresponding to input Geant4 world + VGPlacedVolume* world{nullptr}; + + //! Map of Geant4 logical volumes to VecGeom LV IDs + MapLvVolId volumes; +}; + +//---------------------------------------------------------------------------// +// Convert a Geant4 geometry to a VecGeom geometry. +Converted convert(G4VPhysicalVolume const* world); + +// Convert with custom options +Converted convert(G4VPhysicalVolume const* world, Options options); + +//---------------------------------------------------------------------------// +} // namespace g4vg diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..20f83f6 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,24 @@ +#----------------------------------*-CMake-*----------------------------------# +# Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +#-----------------------------------------------------------------------------# + +file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}" G4VG_SOURCE_DIR) +configure_file(g4vg_test_config.h.in g4vg_test_config.h @ONLY) + +add_executable(g4vg_test + G4VG.test.cc +) +target_link_libraries(g4vg_test + GTest::GTest GTest::gtest_main # For testing + G4VG::g4vg # Code to be tested + VecGeom::vecgeom # To build and check VecGeom objects + ${Geant4_LIBRARIES} # To set up and load Geant4 + Celeritas::geocel # To wrap geant4 exceptions +) +target_include_directories(g4vg_test + PUBLIC "${PROJECT_BINARY_DIR}/test" +) + +#-----------------------------------------------------------------------------# diff --git a/test/G4VG.test.cc b/test/G4VG.test.cc new file mode 100644 index 0000000..bf62869 --- /dev/null +++ b/test/G4VG.test.cc @@ -0,0 +1,166 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file G4VG.test.cc +//---------------------------------------------------------------------------// +#include "G4VG.hh" + +#include +#include +#include +#include +#include +#include + +#include "g4vg_test_config.h" + +using VGLV = vecgeom::LogicalVolume; + +namespace g4vg +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class G4VGTestBase : public ::testing::Test +{ + protected: + virtual std::string basename() const = 0; + + void SetUp() override; + void TearDown() override; + + G4VPhysicalVolume const* g4world() const { return world_; } + + private: + G4VPhysicalVolume* world_{nullptr}; +}; + +//---------------------------------------------------------------------------// +/*! + * Load Geant4 geometry during setup. + */ +void G4VGTestBase::SetUp() +{ + // Guard against loading multiple geometry in the same run + static std::string loaded_basename{}; + std::string this_basename = this->basename(); + + if (!loaded_basename.empty()) + { + if (this_basename != loaded_basename) + { + GTEST_SKIP() << "Cannot run two separate geometries in the same " + "execution: loaded " + << loaded_basename << " but this geometry is " + << this_basename; + } + // Otherwise the loaded file matches the current one; exit early + return; + } + // Set the basename to a temporary value in case something goes wrong + loaded_basename = ""; + + // Construct absolute path to GDML input + std::string filename = g4vg_source_dir; + filename += "/test/data/"; + filename += this_basename; + filename += ".gdml"; + + // Load and strip pointers + celeritas::ScopedGeantExceptionHandler scope_exceptions; + G4GDMLParser gdml_parser; + gdml_parser.SetStripFlag(true); + gdml_parser.Read(filename, /* validate_gdml_schema = */ false); + + // Save world volume + world_ = gdml_parser.GetWorldVolume(); + ASSERT_TRUE(world_) << "GDML parser did not return world volume"; + + // Save the basename + loaded_basename = this_basename; +} + +void G4VGTestBase::TearDown() +{ + vecgeom::GeoManager::Instance().Clear(); +} + +//---------------------------------------------------------------------------// +class SolidsTest : public G4VGTestBase +{ + protected: + std::string basename() const override { return "solids"; } +}; + +TEST_F(SolidsTest, default_options) +{ + auto converted = g4vg::convert(this->g4world()); + ASSERT_TRUE(converted.world); + EXPECT_EQ(25, converted.volumes.size()); + + // Set world in VecGeom manager + auto& vg_manager = vecgeom::GeoManager::Instance(); + vg_manager.RegisterPlacedVolume(converted.world); + vg_manager.SetWorldAndClose(converted.world); + + // Check volumes + std::vector ordered_g4_names; + std::vector ordered_vg_capacities; + + for (auto&& [g4lv, vgid] : converted.volumes) + { + if (vgid >= ordered_g4_names.size()) + { + ordered_g4_names.resize(vgid + 1); + ordered_vg_capacities.resize(ordered_g4_names.size()); + } + + // Save Geant4 name + ASSERT_TRUE(g4lv); + std::string const& g4name = g4lv->GetName(); + ordered_g4_names[vgid] = g4name; + + // Save VecGeom name + auto* vglv = vg_manager.FindLogicalVolume(vgid); + ASSERT_TRUE(vglv); + std::string vgname{vglv->GetName()}; + EXPECT_EQ(0, vgname.find(g4name)) << "Expected Geant4 name '" << g4name + << "' to be at the start of " + "VecGeom name '" + << vgname << "'"; + + // Check volume + auto* vguv = vglv->GetUnplacedVolume(); + ASSERT_TRUE(vguv); + ordered_vg_capacities[vgid] = vguv->Capacity(); + } + + std::vector const expected_g4_names + = {"box500", "cone1", "para1", "sphere1", "parabol1", + "trap1", "trd1", "trd2", "trd3", "trd3_refl", + "tube100", "", "", "", "", + "boolean1", "polycone1", "genPocone1", "ellipsoid1", "tetrah1", + "orb1", "polyhedr1", "hype1", "elltube1", "ellcone1", + "arb8b", "arb8a", "xtru1", "World"}; + EXPECT_EQ(expected_g4_names, ordered_g4_names); + + std::vector const expected_capacities + = {1.25e+08, 1.14982e+08, 3.36e+08, 1.13846e+08, 1.13099e+08, + 1.512e+08, 1.4e+08, 1.4e+08, 1.4e+08, 1.4e+08, + 1.13097e+07, 0, 0, 0, 0, + 1.16994e+08, 2.72926e+07, 2.08567e+08, 4.41582e+07, 1.06667e+08, + 2.68083e+08, 2.23013e+08, 7.75367e+07, 1.50796e+08, 4.96372e+06, + 6.81667e+08, 6.05e+08, 4.505e+06, 1.08e+11}; + ASSERT_EQ(expected_capacities.size(), ordered_vg_capacities.size()); + for (std::size_t i = 0; i != expected_capacities.size(); ++i) + { + EXPECT_NEAR(expected_capacities[i], ordered_vg_capacities[i], 1e6); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace g4vg diff --git a/test/data/solids.gdml b/test/data/solids.gdml new file mode 100644 index 0000000..ebc4706 --- /dev/null +++ b/test/data/solids.gdml @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/g4vg_test_config.h.in b/test/g4vg_test_config.h.in new file mode 100644 index 0000000..32560d3 --- /dev/null +++ b/test/g4vg_test_config.h.in @@ -0,0 +1,14 @@ +/*----------------------------------*-C-*------------------------------------* + * Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. + * See the top-level COPYRIGHT file for details. + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + *---------------------------------------------------------------------------*/ +/*! \file g4vg_test_config.h + * Configuration-specific options for G4VG tests. + *---------------------------------------------------------------------------*/ +#ifndef g4vg_test_config_h +#define g4vg_test_config_h + +static char const g4vg_source_dir[] = "@G4VG_SOURCE_DIR@"; + +#endif /* g4vg_test_config_h */