Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/AdePT/integration/AdePTConfigurationMessenger.hh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ private:
std::unique_ptr<G4UIcmdWithADouble> fSetMillionsOfHitSlotsCmd;
std::unique_ptr<G4UIcmdWithADouble> fSetHitBufferFlushThresholdCmd;
std::unique_ptr<G4UIcmdWithADouble> fSetCPUCapacityFactorCmd;
std::unique_ptr<G4UIcmdWithADouble> fSetHitBufferSafetyFactorCmd;

// Temporary method for setting the VecGeom geometry.
// In the future the geometry will be converted from Geant4 rather than loaded from GDML.
Expand Down
7 changes: 7 additions & 0 deletions src/AdePTConfigurationMessenger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ AdePTConfigurationMessenger::AdePTConfigurationMessenger(AdePTConfiguration *ade
fSetCPUCapacityFactorCmd->SetParameterName("CPUCapacityFactor", false);
fSetCPUCapacityFactorCmd->SetRange("CPUCapacityFactor>=2.5");

fSetHitBufferSafetyFactorCmd = std::make_unique<G4UIcmdWithADouble>("/adept/setHitBufferSafetyFactor", this);
fSetHitBufferSafetyFactorCmd->SetGuidance(
"Sets the HitBuffer safety factor for stalling the GPU. If nParticlesInFlight * HitBufferSafetyFactor > "
"NumHitSlotsLeft, the GPU will stall. Default: 1.5 ");

fSetGDMLCmd = std::make_unique<G4UIcmdWithAString>("/adept/setVecGeomGDML", this);
fSetGDMLCmd->SetGuidance("Temporary method for setting the geometry to use with VecGeom");

Expand Down Expand Up @@ -165,6 +170,8 @@ void AdePTConfigurationMessenger::SetNewValue(G4UIcommand *command, G4String new
fAdePTConfiguration->SetHitBufferFlushThreshold(fSetHitBufferFlushThresholdCmd->GetNewDoubleValue(newValue));
} else if (command == fSetCPUCapacityFactorCmd.get()) {
fAdePTConfiguration->SetCPUCapacityFactor(fSetCPUCapacityFactorCmd->GetNewDoubleValue(newValue));
} else if (command == fSetHitBufferSafetyFactorCmd.get()) {
fAdePTConfiguration->SetHitBufferSafetyFactor(fSetHitBufferSafetyFactorCmd->GetNewDoubleValue(newValue));
} else if (command == fSetGDMLCmd.get()) {
fAdePTConfiguration->SetVecGeomGDML(newValue);
} else if (command == fSetCovfieFileCmd.get()) {
Expand Down
7 changes: 7 additions & 0 deletions test/regression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ add_test(NAME testEm3_validation_WDT
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)

# Testing all UI commands of AdePT
add_test(NAME test_UI_commands
COMMAND bash ${PROJECT_SOURCE_DIR}/test/regression/scripts/test_UI_commands.sh
"$<TARGET_FILE:integrationTest>" "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}" "${SCRIPTS_DIR}" "${TEMP_DIR}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)

set(APP_TESTS

)
79 changes: 39 additions & 40 deletions test/regression/scripts/python_scripts/macro_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,53 @@

import argparse
import os
import re


def generate_macro(template_path, output_path, gdml_name, num_threads, num_events, num_trackslots,
num_leakslots, num_hitslots, gun_type, event_file, track_in_all_regions, regions, wdt_regions):
def generate_macro(template_path, output_path, args):
# Read the template file
with open(template_path, 'r') as template_file:
template_content = template_file.read()

# Substitute placeholders with actual values
macro_content = template_content.replace("$gdml_name", gdml_name)
macro_content = macro_content.replace("$num_threads", str(num_threads))
macro_content = macro_content.replace("$num_events", str(num_events))
macro_content = macro_content.replace("$num_trackslots", str(num_trackslots))
macro_content = macro_content.replace("$num_leakslots", str(num_leakslots))
macro_content = macro_content.replace("$num_hitslots", str(num_hitslots))
macro_content = macro_content.replace("$gun_type", str(gun_type))
macro_content = macro_content.replace(
"$track_in_all_regions", str(track_in_all_regions))

if (str(gun_type) == "hepmc"):
# Validate required placeholders based on template
placeholders = set(re.findall(r"\$(\w+)", template_content))

args_dict = vars(args)

missing = [
p for p in placeholders
if p in args_dict and args_dict[p] is None
]
if missing:
raise ValueError(
f"Missing required arguments for template: {', '.join(missing)}"
)

# Original replacement logic

macro_content = template_content.replace("$gdml_name", str(args.gdml_name))

macro_content = macro_content.replace("$num_threads", str(args.num_threads))
macro_content = macro_content.replace("$num_events", str(args.num_events))
macro_content = macro_content.replace("$num_trackslots", str(args.num_trackslots))
macro_content = macro_content.replace("$num_leakslots", str(args.num_leakslots))
macro_content = macro_content.replace("$num_hitslots", str(args.num_hitslots))
macro_content = macro_content.replace("$gun_type", str(args.gun_type))
macro_content = macro_content.replace("$track_in_all_regions", str(args.track_in_all_regions))

if str(args.gun_type) == "hepmc":
hepmc_part = "/generator/hepmcAscii/maxevents 256 \n\
/generator/hepmcAscii/firstevent 0 \n\
/generator/hepmcAscii/open $event_file \n\
/generator/hepmcAscii/verbose 0"
macro_content = macro_content.replace("$hepmc_part", hepmc_part)
macro_content = macro_content.replace("$event_file", str(event_file))
macro_content = macro_content.replace("$event_file", str(args.event_file))
else:
macro_content = macro_content.replace("$hepmc_part", str(""))

# Regions should be a comma-separated list of region names
region_part = []
for i in regions.split(","):
for i in args.regions.split(","):
region = i.strip()
if region: # Empty regions list or empty region name
region_part.append(f"/adept/addGPURegion {region}")
Expand All @@ -45,7 +60,7 @@ def generate_macro(template_path, output_path, gdml_name, num_threads, num_event

# Woodcock tracking regions should be a comma-separated list of region names
wdt_region_part = []
for i in wdt_regions.split(","):
for i in args.wdt_regions.split(","):
wdt_region = i.strip()
if wdt_region: # Empty wdt regions list or empty region name
wdt_region_part.append(f"/adept/addWDTRegion {wdt_region}")
Expand All @@ -62,16 +77,11 @@ def generate_macro(template_path, output_path, gdml_name, num_threads, num_event
def main():
parser = argparse.ArgumentParser(
description="Generate Geant4 macro file from template.")
parser.add_argument("--template", required=True,
help="Path to the macro template file.")
parser.add_argument("--output", required=True,
help="Path to save the generated macro file.")
parser.add_argument("--gdml_name", required=True,
help="Path to the GDML geometry file.")
parser.add_argument("--num_threads", type=int, required=True,
help="Number of threads to use.")
parser.add_argument("--num_events", type=int, required=True,
help="Number of events to simulate.")
parser.add_argument("--template", help="Path to the macro template file.")
parser.add_argument("--output", help="Path to save the generated macro file.")
parser.add_argument("--gdml_name", help="Path to the GDML geometry file.")
parser.add_argument("--num_threads", type=int, help="Number of threads to use.")
parser.add_argument("--num_events", type=int, help="Number of events to simulate.")
parser.add_argument("--num_trackslots", type=int, default=12,
help="Number of trackslots in million. Should be chosen according to the GPU memory")
parser.add_argument("--num_leakslots", type=float, default=12,
Expand All @@ -81,8 +91,7 @@ def main():
parser.add_argument("--gun_type", default="setDefault",
help="Type of particle gun. Must be 'hepmc' or 'setDefault'.")
parser.add_argument("--event_file", help="Path to the hepmc3 event file")
parser.add_argument("--track_in_all_regions", required=False,
default="True", help="True or False")
parser.add_argument("--track_in_all_regions", default="True", help="True or False")
parser.add_argument("--regions", type=str, required=False, default="",
help="Comma-separated list of regions in which to do GPU transport, only if track_in_all_regions is False")
parser.add_argument("--wdt_regions", type=str, required=False, default="",
Expand All @@ -103,17 +112,7 @@ def main():
generate_macro(
template_path=args.template,
output_path=args.output,
gdml_name=args.gdml_name,
num_threads=args.num_threads,
num_events=args.num_events,
num_trackslots=args.num_trackslots,
num_leakslots=args.num_leakslots,
num_hitslots=args.num_hitslots,
gun_type=args.gun_type,
event_file=args.event_file,
track_in_all_regions=args.track_in_all_regions,
regions=args.regions,
wdt_regions=args.wdt_regions
args=args
)


Expand Down
42 changes: 42 additions & 0 deletions test/regression/scripts/test_UI_commands.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#! /usr/bin/env bash

# SPDX-FileCopyrightText: 2025 CERN
# SPDX-License-Identifier: Apache-2.0

# This is a CI test for testing that all UI commands work without failing

# abort on first encounted error
set -eu -o pipefail


# Read input parameters
ADEPT_EXECUTABLE=$1
PROJECT_BINARY_DIR=$2
PROJECT_SOURCE_DIR=$3
CI_TEST_DIR=$4
CI_TMP_DIR=$5

# Define cleanup function of temporary files
cleanup() {
echo "Cleaning up temporary files..."
rm -rf ${CI_TMP_DIR}
}

# register cleanup to be called on exit
trap cleanup EXIT
# called it directly ensure clean environment
cleanup

# Create temporary directory
mkdir -p ${CI_TMP_DIR}

# use gun_type hepmc or setDefault
$CI_TEST_DIR/python_scripts/macro_generator.py \
--template ${CI_TEST_DIR}/test_ui_commands_template.mac \
--output ${CI_TMP_DIR}/test_ui_commands.mac \
--gdml_name ${PROJECT_SOURCE_DIR}/examples/data/testEm3_regions.gdml \


# run test
$ADEPT_EXECUTABLE -m "${CI_TMP_DIR}/test_ui_commands.mac" --do_validation

155 changes: 155 additions & 0 deletions test/regression/scripts/test_ui_commands_template.mac
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# SPDX-FileCopyrightText: 2023 CERN
# SPDX-License-Identifier: Apache-2.0

## =============================================================================
## Geant4 macro for modelling simplified sampling calorimeters
## =============================================================================

/run/numberOfThreads 1
/control/verbose 0
/run/verbose 0
/process/verbose 0
/tracking/verbose 0
/event/verbose 0

/detector/filename $gdml_name

## =============================================================================
## Testing ALL AdePT UI commands, ordered by topics
## =============================================================================

### MISC

/adept/setVerbosity 0
# Set verbosity level for the AdePT integration layer



### SETTING UP THE GPU

/adept/setMillionsOfTrackSlots 1
# Set the total number of track slots that will be allocated on the GPU, in millions

/adept/setMillionsOfLeakSlots 1
# Set the total number of leak slots that will be allocated on the GPU, in millions

/adept/setMillionsOfHitSlots 1
# Set the total number of hit slots that will be allocated on the GPU, in millions

/adept/setHitBufferThreshold 0.8
# Set the fractional threshold at which the GPU steps are copied from the buffer and not taken
# directly by the G4 workers

/adept/setCPUCapacityFactor 3
# Sets the CPUCapacity factor for scoring with respect to the GPU (see: /adept/setMillionsOfHitSlots). "
# Must at least be 2.5

/adept/setHitBufferSafetyFactor 1.5
# Sets the HitBuffer safety factor for stalling the GPU. If nParticlesInFlight * HitBufferSafetyFactor > "
# NumHitSlotsLeft, the GPU will stall. Default: 1.5

/adept/setCUDAStackLimit 8192



### ------------
### Specifying where the GPU

/adept/setTrackInAllRegions false
# If true, particles are tracked on the GPU across the whole geometry (unless removed via /adept/removeGPURegion )

/adept/addGPURegion Layer1
/adept/addGPURegion Layer2
# Add a region in which transport will be done on GPU

/adept/removeGPURegion Layer3
# Remove a region in which transport will be done on GPU (so it will be done on the CPU)



### ------------
### UserActions
/adept/CallUserSteppingAction true
# If true, the UserSteppingAction is called for on every step. WARNING: The steps are currently not sorted, that
# means it is not guaranteed that the UserSteppingAction is called in order, i.e., it could get called on the
# secondary before the primary has finished its track.
# NOTE: This means that every single step is recorded on GPU and send back to CPU, which can impact performance

/adept/CallUserTrackingAction true
# If true, the PostUserTrackingAction is called for on every track. NOTE: This
# means that the last step of every track is recorded on GPU and send back to CPU



### ------------
### Special Settings:

/adept/setSeed 1242342
# Set the base seed for the rng. Default: 1234567

#/adept/setCovfieBfieldFile
# Set the path the the covfie file for reading in an external magnetic field

/adept/SpeedOfLight false
# If true, all electrons, positrons, gammas handed over to the GPU are immediately killed. That means, for region-based offload,
# that tracking happens normally in non-GPU regions.
# WARNING: Only to be used for testing the speed and fraction of EM, all results are wrong!

/adept/setVecGeomGDML $gdml_name
# Sets the geometry via VecGeom through a gdml.
# WARNING: this will not convert the G4 geometry anymore, so there can be a mismatch between CPU and GPU geometry if used incorrectly!

/adept/FinishLastNParticlesOnCPU 100
# Set N, the number of last N particles per event that are finished on CPU. Default: 0.
# This is an important parameter for handling loopers in a magnetic field, but breaks reproducibility!

/adept/MaxWDTIterations 5
# Set N, the number of maximum Woodcock tracking iterations per step before giving the gamma back to the normal gamma kernel.
# Default: 5. This can be used to optimize the performance in highly granular geometries

### ------------
### Special settings for the AdePTPhysics (ONLY USED INSIDE EXAMPLES) ###

/adept/addWDTRegion Layer1
# Add a region in which the gamma transport is done via Woodcock tracking.
# NOTE: This ONLY applies to the AdePTPhysics, if the PhysicsList uses ANY other physics
# (which is done in LHCb, CMS, ATLAS) then this will have NO effect!

/adept/addWDTKineticEnergyLimit 200 keV
# Sets a kinetic energy limit above which the gamma transport is done via Woodcock tracking in the assigned
# regions. NOTE: This ONLY applies to the AdePTPhysics, if the PhysicsList uses ANY other physics (which is done
# in LHCb, CMS, ATLAS) then this will have NO effect!

/adept/SetMultipleStepsInMSCWithTransportation true
# If true, this configures G4HepEm to use multiple steps in MSC on CPU. This does not affect GPU transport

/adept/SetEnergyLossFluctuation true
# if true, this configures G4HepEm to use energy loss fluctuations. This affects both CPU and GPU transport
# NOTE: This is only true for the AdePTPhysics in the examples! In all other physics lists the setting is
# taken directly from Geant4 and this parameter does not change it.


## =============================================================================
## Finalize the setup and run with the required G4 UI commands
## =============================================================================

/run/setCut 0.7 mm
/run/initialize

/gun/setDefault
/gun/particle e-
/gun/energy 1 keV
/gun/number 1
/gun/position -220 0 0 mm
/gun/direction 1 0 0
/gun/print true

## -----------------------------------------------------------------------------
## Run the simulation with the given number of events and print list of processes
## -----------------------------------------------------------------------------


# run events with parametrised simulation
# by default all created models are active
/run/beamOn 1