Skip to content

Commit e6e68c8

Browse files
committed
added cherenkov example and fixed missing optical properties
1 parent b5ab81b commit e6e68c8

7 files changed

Lines changed: 209 additions & 892 deletions

File tree

api/gconfiguration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ def init_variation(self, newVariation):
217217
self.matFileName = self.system + "__materials_" + str(self.variation) + ".txt"
218218
self.mirFileName = self.system + "__mirrors_" + str(self.variation) + ".txt"
219219

220+
220221
# overwrites any existing geometry file.
221222
def initialize_storage(self):
222223
print()
@@ -256,7 +257,6 @@ def initialize_storage(self):
256257
except OSError as e:
257258
sys.exit(f"Error opening file {self.geoFileName}: {e}")
258259
try:
259-
260260
with open(self.matFileName, "w") as file:
261261
pass
262262
except OSError as e:

examples/meson.build

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ examples_map = {
1717
'b1' : { 'runs' : ['1'], 'variations' : ['default', 'test'] },
1818
'b2' : { 'runs' : ['1', '11'], 'variations' : ['default', 'alt'] },
1919
'b3' : { 'runs' : ['1', '11'], 'variations' : ['default', 'alt'] }
20+
},
21+
'optical' : {
22+
'simple_flux' : { 'runs' : ['1'], 'variations' : ['default', 'CO2', 'C4F10'] },
2023
}
2124
}
2225

@@ -44,7 +47,7 @@ foreach branch : examples_map.keys()
4447
args : [example_dir + example + '.py', '-f', format, '-sql', sql_db, '-v', variation], # the sql flag is ignored for ascii format
4548
workdir : example_dir,
4649
is_parallel : false,
47-
env: test_env,
50+
env : test_env,
4851
priority : 10)
4952
test(run_test_name_asci,
5053
gemc,
@@ -72,7 +75,7 @@ foreach branch : examples_map.keys()
7275
args : [example_dir + example + '.py', '-f', format, '-sql', sql_db, '-r', run_value], # the sql flag is ignored for ascii format
7376
workdir : example_dir,
7477
is_parallel : false,
75-
env: test_env,
78+
env : test_env,
7679
priority : 9)
7780
test(run_test_name_asci,
7881
gemc,
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
3+
from gconfiguration import autogeometry
4+
from gvolume import GVolume
5+
from gmaterial import GMaterial
6+
7+
cfg = autogeometry("examples", "cherenkov")
8+
9+
# Using 3 gases with different refractive indices
10+
variation_material = {
11+
"default": "CF4",
12+
"CO2": "CO2",
13+
"C4F10": "C4F10",
14+
}
15+
16+
photon_energy = "2.0*eV 3.0*eV 4.0*eV 5.0*eV 6.0*eV"
17+
18+
# Optical properties
19+
# Refractive index and absorbtion at 2, 3, 4, 5, 6 eV
20+
rindex = {
21+
"CF4": "1.000481811 1.000487934 1.000496771 1.000508615 1.000523881",
22+
"CO2": "1.000448007 1.000458006 1.000472449 1.000492454 1.000519671",
23+
"C4F10": "1.001306001 1.001329786 1.001364578 1.001412079 1.001474826",
24+
}
25+
absorption = {
26+
"CF4": "100*m 100*m 100*m 100*m 100*m",
27+
"CO2": "50*m 50*m 40*m 30*m 20*m",
28+
"C4F10": "6*m 6*m 6*m 5*m 4*m",
29+
}
30+
31+
material_definitions = {
32+
"CF4": {
33+
"name": "CF4",
34+
"description": "CF4 gas with optical properties",
35+
# g/cm3, approximately 20 C and 1 atm
36+
"density": 0.003658,
37+
"atoms": [("C", 1), ("F", 4)],
38+
},
39+
40+
"CO2": {
41+
"name": "CO2",
42+
"description": "CO2 gas at 1 atm with optical properties",
43+
"density": 0.001842,
44+
"atoms": [("C", 1), ("O", 2)],
45+
},
46+
47+
"C4F10": {
48+
"name": "C4F10",
49+
"description": "C4F10 gas with optical properties",
50+
"density": 0.00973,
51+
"atoms": [("C", 4), ("F", 10)],
52+
},
53+
}
54+
55+
# common panel geometry
56+
panels = [
57+
("BackplateLeft", -255, 0, 245, 500, 1),
58+
("BackplateRight", 255, 0, 245, 500, 2),
59+
("BackplateBottom", 0, -255, 500, 245, 3),
60+
("BackplateTop", 0, 255, 500, 245, 4),
61+
]
62+
63+
for variation, material in variation_material.items():
64+
cfg.init_variation(variation)
65+
66+
mat = GMaterial(material_definitions[material]["name"])
67+
mat.description = material_definitions[material]["description"]
68+
mat.density = material_definitions[material]["density"]
69+
70+
for element, natoms in material_definitions[material]["atoms"]:
71+
mat.addNAtoms(element, natoms)
72+
73+
mat.photonEnergy = photon_energy
74+
mat.indexOfRefraction = rindex[material]
75+
mat.absorptionLength = absorption[material]
76+
mat.publish(cfg)
77+
78+
# World
79+
world = GVolume("root")
80+
world.description = "Air world for Cherenkov validation"
81+
world.make_box(600, 600, 600)
82+
world.material = "G4_AIR"
83+
world.color = "ghostwhite"
84+
world.style = 0
85+
world.publish(cfg)
86+
87+
radiator = GVolume("radiator")
88+
radiator.mother = "root"
89+
radiator.description = f"1 m cube radiator using {material}"
90+
radiator.make_box(500, 500, 500)
91+
radiator.material = material
92+
radiator.color = "66ccff"
93+
radiator.opacity = 0.1
94+
radiator.publish(cfg)
95+
96+
for name, x, y, half_x, half_y, panel_id in panels:
97+
backplate = GVolume(name)
98+
backplate.variation = variation
99+
backplate.mother = "radiator"
100+
backplate.description = "Downstream optical photon collection panel"
101+
backplate.make_box(half_x, half_y, 0.5)
102+
backplate.set_position(x, y, 499)
103+
104+
# Use same gas as the radiator so optical photons are not killed
105+
# by crossing into a material without matching optical properties.
106+
backplate.material = material
107+
108+
backplate.color = "lightgray"
109+
backplate.digitization = "flux"
110+
backplate.set_identifier("detector", 1)
111+
backplate.publish(cfg)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
experiment: examples
2+
runno: 1
3+
n: 100
4+
nthreads: 1
5+
6+
# Required for optical-photon flux hits because optical photons deposit zero energy.
7+
recordZeroEdep: true
8+
9+
gparticle:
10+
- name: e-
11+
p: 1000
12+
vz: -50
13+
14+
gsystem:
15+
- name: cherenkov
16+
17+
gstreamer:
18+
- format: csv
19+
filename: out
20+
- format: root
21+
filename: out
22+
23+
phys_list: FTFP_BERT + G4OpticalPhysics

gdetector/gdetectorConstruction.cc

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "G4RunManager.hh"
2121
#include "G4UserLimits.hh"
2222

23-
G4ThreadLocal GMagneto* GDetectorConstruction::gmagneto = nullptr;
23+
G4ThreadLocal GMagneto *GDetectorConstruction::gmagneto = nullptr;
2424

2525
GDetectorConstruction::GDetectorConstruction(std::shared_ptr<GOptions> gopts)
2626
: GBase(gopts, GDETECTOR_LOGGER),
@@ -31,7 +31,7 @@ GDetectorConstruction::GDetectorConstruction(std::shared_ptr<GOptions> gopts)
3131
}
3232

3333
// Builds (or rebuilds) the GEMC world and then the Geant4 world.
34-
G4VPhysicalVolume* GDetectorConstruction::Construct() {
34+
G4VPhysicalVolume *GDetectorConstruction::Construct() {
3535
log->debug(NORMAL, FUNCTION_NAME);
3636

3737
// Clean any old geometry.
@@ -52,8 +52,7 @@ G4VPhysicalVolume* GDetectorConstruction::Construct() {
5252
if (gsystems.empty()) {
5353
log->debug(NORMAL, FUNCTION_NAME, "creating world from options");
5454
gworld = std::make_shared<GWorld>(gopt);
55-
}
56-
else {
55+
} else {
5756
log->debug(NORMAL, FUNCTION_NAME, "creating world from a gsystem vector of size ", gsystems.size());
5857
gworld = std::make_shared<GWorld>(gopt, gsystems);
5958
}
@@ -65,8 +64,8 @@ G4VPhysicalVolume* GDetectorConstruction::Construct() {
6564

6665
// tally with number :
6766
log->info(0, "Tally summary: \n - ", gworld->get_number_of_volumes() - 1, " volumes\n - ",
68-
g4world->number_of_volumes(), " geant4 built volumes\n - ",
69-
nsdetectors, " sensitive detectors\n");
67+
g4world->number_of_volumes(), " geant4 built volumes\n - ",
68+
nsdetectors, " sensitive detectors\n");
7069

7170

7271
// Return the physical volume for the ROOT world volume.
@@ -81,28 +80,27 @@ void GDetectorConstruction::ConstructSDandField() {
8180

8281
// Local cache of sensitive detectors keyed by digitization name.
8382
// Multiple volumes can share the same digitization name and therefore reuse one SD instance.
84-
std::unordered_map<std::string, GSensitiveDetector*> sensitiveDetectorsMap;
83+
std::unordered_map<std::string, GSensitiveDetector *> sensitiveDetectorsMap;
8584

8685
// Loop over all systems and their volumes.
87-
for (const auto& [systemName, gsystemPtr] : *gworld->getSystemsMap()) {
88-
for (const auto& [volumeName, gvolumePtr] : gsystemPtr->getGVolumesMap()) {
89-
auto const& digitizationName = gvolumePtr->getDigitization();
90-
auto const& g4name = gvolumePtr->getG4Name();
91-
auto* g4volume = g4world->getG4Volume(g4name)->getLogical();
86+
for (const auto &[systemName, gsystemPtr]: *gworld->getSystemsMap()) {
87+
for (const auto &[volumeName, gvolumePtr]: gsystemPtr->getGVolumesMap()) {
88+
auto const &digitizationName = gvolumePtr->getDigitization();
89+
auto const &g4name = gvolumePtr->getG4Name();
90+
auto *g4volume = g4world->getG4Volume(g4name)->getLogical();
9291

9392
// Ensure the Geant4 logical volume exists.
9493
// Some GEMC volumes can be "copy-of" another volume; in that case, reuse the
9594
// referenced Geant4 logical volume rather than failing.
9695
if (g4volume == nullptr) {
9796
std::string copyOf = gvolumePtr->getCopyOf();
9897
if (copyOf != "" && copyOf != UNINITIALIZEDSTRINGQUANTITY) {
99-
auto gsystem = gvolumePtr->getSystem();
100-
auto volume_copy = gsystem + "/" + copyOf;
98+
auto gsystem = gvolumePtr->getSystem();
99+
auto volume_copy = gsystem + "/" + copyOf;
101100
auto copyG4Volume = g4world->getG4Volume(volume_copy)->getLogical();
102-
if (copyG4Volume != nullptr) { g4volume = copyG4Volume; }
103-
else {
101+
if (copyG4Volume != nullptr) { g4volume = copyG4Volume; } else {
104102
log->error(ERR_GVOLUMENOTFOUND, FUNCTION_NAME,
105-
" Logical volume copy <" + volume_copy + "> not found.");
103+
" Logical volume copy <" + volume_copy + "> not found.");
106104
}
107105
}
108106
}
@@ -117,18 +115,17 @@ void GDetectorConstruction::ConstructSDandField() {
117115
log->info(2, "Creating new sensitive detector <", digitizationName, "> for volume <", g4name, ">");
118116

119117
sensitiveDetectorsMap[digitizationName] = new GSensitiveDetector(digitizationName, gopt);
120-
}
121-
else {
118+
} else {
122119
log->info(2, "Sensitive detector <", digitizationName,
123-
"> is already created and available for volume <", g4name, ">");
120+
"> is already created and available for volume <", g4name, ">");
124121
}
125122

126123
// Register the volume touchable with the sensitive detector.
127124
// The touchable encodes identity and dimension metadata needed by digitization.
128-
const auto& vdimensions = gvolumePtr->getDetectorDimensions();
129-
const auto& identity = gvolumePtr->getGIdentity();
130-
const auto& mass = g4volume->GetMass();
131-
auto this_gtouchable = std::make_shared<
125+
const auto &vdimensions = gvolumePtr->getDetectorDimensions();
126+
const auto &identity = gvolumePtr->getGIdentity();
127+
const auto &mass = g4volume->GetMass();
128+
auto this_gtouchable = std::make_shared<
132129
GTouchable>(gopt, digitizationName, identity, vdimensions, mass);
133130
sensitiveDetectorsMap[digitizationName]->registerGVolumeTouchable(g4name, this_gtouchable);
134131

@@ -141,17 +138,17 @@ void GDetectorConstruction::ConstructSDandField() {
141138
//g4volume->SetUserLimits(new G4UserLimits(0.1*mm, 0.1*mm));
142139

143140
log->info(2, "Logical Volume <" + g4name + "> has been successfully assigned to SD.",
144-
sensitiveDetectorsMap[digitizationName]);
141+
sensitiveDetectorsMap[digitizationName]);
145142
}
146143

147144
// Process electromagnetic fields.
148145
// If a volume declares an EM field, ensure the field container exists and install
149146
// a per-volume field manager configured by the named field map.
150-
const auto& field_name = gvolumePtr->getEMField();
147+
const auto &field_name = gvolumePtr->getEMField();
151148
if (field_name != "" && field_name != UNINITIALIZEDSTRINGQUANTITY) {
152149
if (gmagneto == nullptr) { gmagneto = new GMagneto(gopt); }
153150
log->info(2, "Volume <", volumeName, "> has field: <", field_name,
154-
">. Looking into field map definitions.");
151+
">. Looking into field map definitions.");
155152
log->info(2, "Setting field manager for volume <", g4name, "> with field <", field_name, ">");
156153
g4world->setFieldManagerForVolume(g4name, gmagneto->getFieldMgr(field_name).get(), true);
157154
}
@@ -164,22 +161,22 @@ void GDetectorConstruction::ConstructSDandField() {
164161

165162
// Bind each digitization routine to its corresponding sensitive detector.
166163
const auto sdetectors = gworld->getSensitiveDetectorsList();
167-
for (auto& sdname : sdetectors) {
168-
auto digitization_routine = digitization_routines_map->at(sdname);
169-
double maxStep = digitization_routine->readoutSpecs->getMaxStep();
164+
for (auto &sdname: sdetectors) {
165+
auto digitization_routine = digitization_routines_map->at(sdname);
166+
double maxStep = digitization_routine->readoutSpecs->getMaxStep();
170167

171168
sensitiveDetectorsMap[sdname]->assign_digi_routine(digitization_routine);
172169
log->info(1, "Digitization routine <" + sdname + "> has been successfully assigned to SD.",
173-
sensitiveDetectorsMap[sdname]);
170+
sensitiveDetectorsMap[sdname]);
174171

175172
// Loop over all systems and their volumes.
176173
// and assign max step to the corresponding logical volume
177-
for (const auto& [systemName, gsystemPtr] : *gworld->getSystemsMap()) {
178-
for (const auto& [volumeName, gvolumePtr] : gsystemPtr->getGVolumesMap()) {
179-
auto const& digitizationName = gvolumePtr->getDigitization();
174+
for (const auto &[systemName, gsystemPtr]: *gworld->getSystemsMap()) {
175+
for (const auto &[volumeName, gvolumePtr]: gsystemPtr->getGVolumesMap()) {
176+
auto const &digitizationName = gvolumePtr->getDigitization();
180177
if (digitizationName == sdname) {
181-
auto const& g4name = gvolumePtr->getG4Name();
182-
auto* g4volume = g4world->getG4Volume(g4name)->getLogical();
178+
auto const &g4name = gvolumePtr->getG4Name();
179+
auto *g4volume = g4world->getG4Volume(g4name)->getLogical();
183180

184181
// g4volume->SetUserLimits(new G4UserLimits(maxStep, maxStep)); // this will also kill track cause
185182
// the second argument is max track length
@@ -189,28 +186,24 @@ void GDetectorConstruction::ConstructSDandField() {
189186
}
190187
}
191188
}
192-
193189
}
194190
}
195191

196192
// Loads (or dynamically resolves) the digitization routine for each sensitive detector.
197193
void GDetectorConstruction::loadDigitizationPlugins() {
198194
const auto sdetectors = gworld->getSensitiveDetectorsList();
199195

200-
for (auto& sdname : sdetectors) {
196+
for (auto &sdname: sdetectors) {
201197
if (sdname == FLUXNAME) {
202198
log->info(1, "Loading flux digitization plugin for routine <" + sdname + ">");
203199
digitization_routines_map->emplace(sdname, std::make_shared<GFluxDigitization>(gopt));
204-
}
205-
else if (sdname == COUNTERNAME) {
200+
} else if (sdname == COUNTERNAME) {
206201
log->info(1, "Loading particle counter digitization plugin for routine <" + sdname + ">");
207202
digitization_routines_map->emplace(sdname, std::make_shared<GParticleCounterDigitization>(gopt));
208-
}
209-
else if (sdname == DOSIMETERNAME) {
203+
} else if (sdname == DOSIMETERNAME) {
210204
log->info(1, "Loading dosimeter digitization plugin for routine <" + sdname + ">");
211205
digitization_routines_map->emplace(sdname, std::make_shared<GDosimeterDigitization>(gopt));
212-
}
213-
else {
206+
} else {
214207
// if it's not in the map already, add it
215208
log->info(0, "Loading new digitization plugin for routine <" + sdname + ">");
216209
digitization_routines_map->emplace(sdname, gdynamicdigitization::load_dynamicRoutine(sdname, gopt));
@@ -221,8 +214,7 @@ void GDetectorConstruction::loadDigitizationPlugins() {
221214

222215
if (digitization_routines_map->at(sdname)->defineReadoutSpecs()) {
223216
log->info(1, "Digitization routine <" + sdname + "> has been successfully defined.");
224-
}
225-
else { log->error(ERR_DEFINESPECFAIL, "defineReadoutSpecs failure for <" + sdname + ">"); }
217+
} else { log->error(ERR_DEFINESPECFAIL, "defineReadoutSpecs failure for <" + sdname + ">"); }
226218
}
227219
}
228220

@@ -242,6 +234,5 @@ void GDetectorConstruction::reload_geometry(SystemList sl) {
242234
if (rm) {
243235
rm->DefineWorldVolume(Construct());
244236
ConstructSDandField();
245-
}
246-
else { log->error(1, "GDetectorConstruction::reload_geometry", "Geant4 Run manager not found."); }
237+
} else { log->error(1, "GDetectorConstruction::reload_geometry", "Geant4 Run manager not found."); }
247238
}

0 commit comments

Comments
 (0)