Skip to content

Commit

Permalink
feat: material map generation and validation scripts and pipeline job (
Browse files Browse the repository at this point in the history
…#677)

### Briefly, what does this PR introduce?
This PR adds automatic material map generation and validation (including
comparison with what happens if we reuse the previous field map). It
runs this in a pipeline job.

All this is based on @ShujieL's scripts in
https://github.com/eic/snippets/tree/main/Tracking/material_map. It
adds:
- automatic download of Acts source files,
- less copied code, more calling of Acts source,
- (therefore) less flexibility in file names.

### What kind of change does this PR introduce?
- [ ] Bug fix (issue #__)
- [x] New feature (issue #__)
- [ ] Documentation update
- [ ] Other: __

### Please check if this PR fulfills the following:
- [ ] Tests for the changes have been added
- [ ] Documentation has been added / updated
- [x] Changes have been communicated to collaborators @ShujieL 

### Does this PR introduce breaking changes? What changes might users
need to make to their code?
No.

### Does this PR change default behavior?
No.

---------

Co-authored-by: Dmitry Kalinkin <[email protected]>
  • Loading branch information
wdconinc and veprbl authored Apr 5, 2024
1 parent fc55362 commit d17a5c7
Show file tree
Hide file tree
Showing 9 changed files with 504 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/linux-eic-shell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,37 @@ jobs:
root -b -q "scripts/test_ACTS.cxx+(\"${DETECTOR_PATH}/${DETECTOR_CONFIG}.xml\")" | tee check_tracking_geometry.out
bin/acts_geo_check check_tracking_geometry.out
validate-material-map:
runs-on: ubuntu-latest
needs:
- build
- check-tracking-geometry
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: build-gcc-full-eic-shell
path: install/
- uses: cvmfs-contrib/github-action-cvmfs@v4
- uses: eic/run-cvmfs-osg-eic-shell@main
with:
platform-release: "jug_xl:nightly"
setup: install/setup.sh
run: |
pushd scripts/material_map
export DETECTOR_CONFIG=epic_craterlake_material_map
./run_material_map_validation.sh
popd
- uses: actions/upload-artifact@v4
with:
name: material_map
path: |
"scripts/material_map/*.json"
"scripts/material_map/*.root"
scripts/material_map/Surfaces/
scripts/material_map/Validation/
if-no-files-found: error

convert-to-gdml:
runs-on: ubuntu-latest
needs:
Expand Down
5 changes: 5 additions & 0 deletions scripts/material_map/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Examples/
Surfaces/
Validation/
calibrations/
*.json
49 changes: 49 additions & 0 deletions scripts/material_map/epic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

## Stand alone function to build ePIC geometry with ACTS python bindings
## for material mapping
## Shujie Li, 03, 2024

from pathlib import Path

import acts
import acts.examples.dd4hep

from acts import (
Vector4,
MaterialMapJsonConverter
)

import json

def getDetector(
xmlFile,
jsonFile="",
logLevel=acts.logging.WARNING,
):
customLogLevel = acts.examples.defaultLogging(logLevel=logLevel)
logger = acts.logging.getLogger("epic.getDetector")

matDeco = None
if len(jsonFile)>0:
file = Path(jsonFile)
logger.info("Adding material from %s", file.absolute())
matDeco = acts.IMaterialDecorator.fromFile(
file,
level=customLogLevel(maxLevel=acts.logging.INFO),
)

dd4hepConfig = acts.examples.dd4hep.DD4hepGeometryService.Config(
xmlFileNames=[xmlFile],
logLevel=logLevel,
dd4hepLogLevel=customLogLevel(),
)
detector = acts.examples.dd4hep.DD4hepDetector()

config = acts.MaterialMapJsonConverter.Config()

trackingGeometry, deco = detector.finalize(dd4hepConfig, matDeco)

return detector, trackingGeometry, deco
40 changes: 40 additions & 0 deletions scripts/material_map/geometry_epic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

import os
import argparse

import acts

import epic
from geometry import runGeometry

if "__main__" == __name__:
p = argparse.ArgumentParser(
description="Script to generate geometry-map.json for ePIC geometry"
)
p.add_argument(
"-i",
"--xmlFile",
default=(
os.environ.get("DETECTOR_PATH", "")
+ "/"
+ os.environ.get("DETECTOR_CONFIG", "")
+ ".xml"
),
help="Input xml file containing ePIC geometry",
)
args = p.parse_args()

detector, trackingGeometry, decorators = epic.getDetector(args.xmlFile)

runGeometry(
trackingGeometry,
decorators,
outputDir=os.getcwd(),
outputObj=False,
outputCsv=False,
outputJson=True,
outputRoot=True,
)
51 changes: 51 additions & 0 deletions scripts/material_map/material_mapping_epic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

import os
import argparse

import acts
from acts.examples import JsonFormat

import epic
from material_mapping import runMaterialMapping

if "__main__" == __name__:

p = argparse.ArgumentParser(
description="Script to generate material map for ePIC geometry"
)
p.add_argument(
"--xmlFile",
default=os.environ.get("DETECTOR_PATH", "")+"epic_craterlake.xml",
help="input xml file containing ePIC geometry",
)
p.add_argument(
"--geoFile",
type=str,
default="geometry-map.json",
help="input json file to define volumes and layers used in material mapping",
)
p.add_argument(
"--matFile",
type=str,
default="material-map.json",
help="output filename for the generated material map, can be json and cbor formats",
)
args = p.parse_args()

mapName = args.matFile.split('.')[0]

detector, trackingGeometry, decorators = epic.getDetector(
args.xmlFile, args.geoFile)

runMaterialMapping(
trackingGeometry,
decorators,
outputDir = os.getcwd(),
inputDir = os.getcwd(),
readCachedSurfaceInformation=False,
mapVolume= False,
mapName = mapName,
).run()
77 changes: 77 additions & 0 deletions scripts/material_map/material_recording_epic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

import os
import warnings
from pathlib import Path
import argparse

import acts
from acts.examples import (
GaussianVertexGenerator,
ParametricParticleGenerator,
FixedMultiplicityGenerator,
EventGenerator,
RandomNumbers,
)

import acts.examples.dd4hep
import acts.examples.geant4
import acts.examples.geant4.dd4hep

import epic
from material_recording import runMaterialRecording

u = acts.UnitConstants

_material_recording_executed = False


def main():

p = argparse.ArgumentParser()
p.add_argument(
"-n", "--events", type=int, default=1000, help="Number of events to generate"
)
p.add_argument(
"-t", "--tracks", type=int, default=100, help="Particle tracks per event"
)
p.add_argument(
"-i", "--xmlFile", type=str, default=os.environ.get("DETECTOR_PATH", "") + os.environ.get("DETECTOR_CONFIG", "") + ".xml", help="DD4hep input file"
)
p.add_argument(
"-o", "--outputName", type=str, default="geant4_material_tracks.root", help="Name of the output rootfile"
)
p.add_argument(
"--eta_min",
type=float,
default=-8.0,
help="eta min (optional)",
)
p.add_argument(
"--eta_max",
type=float,
default=8.0,
help="eta max (optional)",
)
args = p.parse_args()

detector, trackingGeometry, decorators = epic.getDetector(
args.xmlFile)

detectorConstructionFactory = (
acts.examples.geant4.dd4hep.DDG4DetectorConstructionFactory(detector)
)

runMaterialRecording(
detectorConstructionFactory=detectorConstructionFactory,
tracksPerEvent=args.tracks,
outputDir=os.getcwd(),
etaRange=(args.eta_min, args.eta_max),
s=acts.examples.Sequencer(events=args.events, numThreads=1),
).run()


if "__main__" == __name__:
main()
54 changes: 54 additions & 0 deletions scripts/material_map/material_validation_epic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

import os
import argparse

import acts
import acts.examples.dd4hep
from acts.examples import Sequencer

import epic
from material_validation import runMaterialValidation


if "__main__" == __name__:

p = argparse.ArgumentParser(
description="Script to produce propogation validation for ePIC material mapping."
)
p.add_argument(
"--xmlFile",
default=os.environ.get("DETECTOR_PATH", "") + os.environ.get("DETECTOR_CONFIG", "") + ".xml",
help="input xml file containing ePIC geometry",
)
p.add_argument(
"--matFile",
type=str,
default="material-map.json",
help="input material map file, can be either Json or Cbor",
)
p.add_argument(
"--outputName",
type=str,
default="propagation-material.root",
help="customized name of the output rootfile",
)
p.add_argument(
"-n","--nevents",
type=int,
default=100,
help="number of events to run",
)
args = p.parse_args()

detector, trackingGeometry, decorators = epic.getDetector(args.xmlFile, args.matFile)

field = acts.ConstantBField(acts.Vector3(0, 0, 0))

runMaterialValidation(
trackingGeometry, decorators, field,
outputDir=os.getcwd(), outputName=args.outputName,
s=Sequencer(events=args.nevents, numThreads=-1)
).run()
70 changes: 70 additions & 0 deletions scripts/material_map/materialmap_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2024 Shujie Li

## Generate ePIC material map for ACTS
## read config-map.json and turn on mapping for approach 1 and 2 of each sensitive surface.
import pandas as pd
import numpy as np
import json
import os
import argparse

if "__main__" == __name__:
p = argparse.ArgumentParser(
description="Script to turn on all approach 1 and 2, and also the beampipe surface in config json file for matieral mapping"
)
p.add_argument(
"-i","--inputFile",
type=str,
default="config-map.json",
help=" input json file to be modified",
)
p.add_argument(
"-o","--outputFile",
type=str,
default="config-map_new.json",
help=" output json file",
)

args = p.parse_args()
print(args)
fname = args.inputFile
out_name = args.outputFile


## load json file
f = open(fname)
dd = json.load(f)

ee=dd['Volumes']['entries']

## print volume name and ID
print ("Volume ID Name Approaches")
for vv in np.arange(len(ee)):
nn = ee[vv]['value']['NAME']

if "|" not in nn and "Gap" not in nn:
print(ee[vv]['volume'], nn,"1, 2")#print(ee[vv]['value'])#['NAME'])
if "acts_beampipe_central::Barrel" in nn:
v_beampipe = vv+1
print(v_beampipe, nn, "X")

## find apporach 1 and 2 to turn on mapping
for vv in np.arange(1,1+len(dd['Surfaces'])):
for ii,tt in enumerate(dd['Surfaces'][str(vv)]):
if 'approach' in tt:
dd['Surfaces'][str(vv)][ii]['value']['material']['mapMaterial']=True
## turn on beampipe surface and defind binning
elif vv==v_beampipe:
if tt['value']['bounds']['type']=='CylinderBounds':
# print (dd['Surfaces'][str(vv)][ii])
dd['Surfaces'][str(vv)][ii]['value']['material']['mapMaterial']=True
dd['Surfaces'][str(vv)][ii]['value']['material']['binUtility']['binningdata'][0]['bins']=36
dd['Surfaces'][str(vv)][ii]['value']['material']['binUtility']['binningdata'][1]['bins']=200


with open(out_name, "w") as outfile:
json.dump(dd, outfile, indent=4)

print("Done! Updated config file at "+out_name)
Loading

0 comments on commit d17a5c7

Please sign in to comment.