2#include <gemc/gbase/gbase.h>
4#include "G4AutoLock.hh"
5#include "G4BuilderType.hh"
6#include "G4MTRunManager.hh"
7#include "G4ParticleTable.hh"
8#include "G4PhysListFactoryAlt.hh"
9#include "G4PhysicsConstructorRegistry.hh"
10#include "G4ProcessManager.hh"
11#include "G4ProcessVector.hh"
12#include "G4RunManager.hh"
13#include "G4StateManager.hh"
14#include "G4StepLimiterPhysics.hh"
15#include "G4SystemOfUnits.hh"
16#include "G4UIcmdWithoutParameter.hh"
17#include "G4UIdirectory.hh"
18#include "G4UImanager.hh"
19#include "G4UImessenger.hh"
20#include "G4VPhysicsConstructor.hh"
21#include "G4VProcess.hh"
33G4Mutex constructionMutex = G4MUTEX_INITIALIZER;
35bool editableState(std::string& error) {
36 if (G4Threading::IsWorkerThread()) {
37 error =
"Physics changes must be requested on the master thread.";
40 const auto state = G4StateManager::GetStateManager()->GetCurrentState();
41 if (state == G4State_PreInit || state == G4State_Idle) {
return true; }
42 error =
"Physics can only be changed before initialization or between runs.";
46void flushWorkerCommands() {
47 if (
auto* master =
dynamic_cast<G4MTRunManager*
>(G4RunManager::GetRunManager())) {
48 master->RequestWorkersProcessCommandsStack();
52template<
typename Function>
53void forManagers(Function function) {
54 std::set<G4ProcessManager*> seen;
55 auto* iterator = G4ParticleTable::GetParticleTable()->GetIterator();
56 iterator->reset(
false);
57 while ((*iterator)()) {
58 auto* manager = iterator->value()->GetProcessManager();
59 if (manager && seen.insert(manager).second) { function(manager); }
63std::string category(
const G4VProcess* process) {
65 if (process->GetProcessName() ==
"Cerenkov" || process->GetProcessName() ==
"Scintillation") {
68 switch (process->GetProcessType()) {
69 case fTransportation:
return "Transportation";
70 case fElectromagnetic:
return "Electromagnetic";
71 case fOptical:
return "Optical";
72 case fHadronic:
return "Hadronic";
73 case fDecay:
return "Decay";
74 case fGeneral:
return "General";
75 case fParameterisation:
return "Parameterisation";
76 case fParallel:
return "Parallel worlds";
77 default:
return "Other";
86 directory = std::make_unique<G4UIdirectory>(
"/gemc/physics/");
87 clear = command(
"clearProcesses");
88 rebuild = command(
"rebuildProcesses");
89 activate = command(
"applyActivation");
92 if (input == activate.get()) { physics.applyActivation(); }
93 else if (G4Threading::IsWorkerThread()) {
94 if (input == clear.get()) { physics.clearProcesses(); }
95 if (input == rebuild.get()) { physics.rebuildWorker(); }
99 std::unique_ptr<G4UIcmdWithoutParameter> command(
const std::string& name) {
100 const auto path =
"/gemc/physics/" + name;
101 auto result = std::make_unique<G4UIcmdWithoutParameter>(path.c_str(),
this);
102 result->AvailableForStates(G4State_PreInit, G4State_Idle);
103 result->SetToBeBroadcasted(
true);
106 gphysics::RuntimePhysicsList& physics;
107 std::unique_ptr<G4UIdirectory> directory;
108 std::unique_ptr<G4UIcmdWithoutParameter> clear, rebuild, activate;
119 if (!
select(name, error)) {
throw std::runtime_error(error); }
120 messenger = std::make_unique<GPhysicsMessenger>(*
this);
125 G4VMPLsubInstanceManager.offset[g4vmplInstanceID].physicsVector->clear();
129 auto* manager = G4RunManager::GetRunManager();
131 const_cast<G4VUserPhysicsList*
>(manager->GetUserPhysicsList())) :
nullptr;
135 g4alt::G4PhysListFactory factory;
136 const auto& names = factory.AvailablePhysLists();
137 return {names.begin(), names.end()};
141 const auto names = G4PhysicsConstructorRegistry::Instance()->AvailablePhysicsConstructors();
142 return {names.begin(), names.end()};
146 if (!editableState(error)) {
return false; }
147 std::string normalized;
148 std::copy_if(name.begin(), name.end(), std::back_inserter(normalized),
149 [](
unsigned char ch) { return !std::isspace(ch); });
150 if (normalized == selected) {
return true; }
151 std::set<std::string> additions;
152 for (
auto start = normalized.find(
'+'); start != std::string::npos;) {
153 const auto end = normalized.find(
'+', start + 1);
154 const auto constructor = normalized.substr(start + 1, end - start - 1);
155 if (constructor.empty() || !additions.insert(constructor).second) {
156 error =
"Physics constructors must be nonempty and cannot be added twice.";
161 g4alt::G4PhysListFactory factory;
162 if (!factory.IsReferencePhysList(normalized)) {
163 error =
"Unknown physics list or constructor: " + name;
166 auto* stateManager = G4StateManager::GetStateManager();
167 const auto previousState = stateManager->GetCurrentState();
168 if (previousState == G4State_Idle) { flushWorkerCommands(); }
170 stateManager->SetNewState(G4State_PreInit);
171 std::unique_ptr<G4VModularPhysicsList> next(factory.GetReferencePhysList(normalized));
172 next->RegisterPhysics(
new G4StepLimiterPhysics());
173 auto reject = [&](
const std::string& message) {
175 lists.push_back(std::move(next));
176 if (!selected.empty()) {
178 lists.emplace_back(factory.GetReferencePhysList(selected));
180 stateManager->SetNewState(previousState);
184 for (
const auto& addition : additions) {
185 if (!addition.starts_with(
"G4")) {
continue; }
186 bool installed =
false;
187 for (
int i = 0;
const auto* constructor = next->GetPhysics(i); ++i) {
188 installed |=
demangle(
typeid(*constructor).name()) == addition;
191 return reject(
"Constructor conflicts with the reference list: " + addition +
192 ". Select a reference-list variant or use replacement syntax instead.");
195 const auto* em = next->GetPhysicsWithType(bElectromagnetic);
196 const std::string identity = em ? std::string(
typeid(*em).name()) + em->GetPhysicsName() :
"";
197 if (previousState == G4State_Idle && identity != electromagneticIdentity) {
198 return reject(
"Changing the electromagnetic constructor after initialization requires restarting GEMC.");
200 stateManager->SetNewState(previousState);
201 if (previousState == G4State_Idle) {
202 broadcast(
"clearProcesses");
205 activeConstructors.clear();
206 for (
int i = 0;
const auto* constructor = next->GetPhysics(i); ++i) {
207 activeConstructors.push_back(
const_cast<G4VPhysicsConstructor*
>(constructor));
209 *G4VMPLsubInstanceManager.offset[g4vmplInstanceID].physicsVector = activeConstructors;
211 defaultCutValue = productionCut.value_or(next->GetDefaultCutValue());
212 activeList = next.get();
213 lists.push_back(std::move(next));
214 selected = normalized;
215 electromagneticIdentity = identity;
216 if (previousState == G4State_Idle) {
217 SetDefaultCutValue(defaultCutValue);
219 auto* manager = G4RunManager::GetRunManager();
220 manager->InitializePhysics();
221 manager->PhysicsHasBeenModified();
222 broadcast(
"rebuildProcesses");
223 broadcast(
"applyActivation");
229 for (
auto* constructor : activeConstructors) { constructor->ConstructParticle(); }
233 G4AutoLock lock(&constructionMutex);
235 const_cast<G4VPCManager&
>(G4VPhysicsConstructor::GetSubInstanceManager()).NewSubInstances();
236 auto& state = threadState.Get();
237 if (!state.initialized) {
239 forManagers([&](G4ProcessManager* manager) {
240 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
241 state.retained.insert((*manager->GetProcessList())[i]);
246 const auto transport = std::find_if(state.retained.begin(), state.retained.end(),
247 [](
const auto* process) { return process->GetProcessType() == fTransportation; });
248 forManagers([&](G4ProcessManager* manager) {
249 if (manager->GetProcessListLength() == 0 && transport != state.retained.end()) {
250 manager->AddProcess(*transport);
251 manager->SetProcessOrderingToFirst(*transport, idxAlongStep);
252 manager->SetProcessOrderingToFirst(*transport, idxPostStep);
256 for (
auto* constructor : activeConstructors) {
257 const bool electromagnetic = constructor->GetPhysicsType() == bElectromagnetic;
258 if (electromagnetic && state.initialized) {
continue; }
259 std::set<G4VProcess*> before;
260 if (electromagnetic) {
261 forManagers([&](G4ProcessManager* manager) {
262 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
263 before.insert((*manager->GetProcessList())[i]);
267 constructor->ConstructProcess();
268 if (electromagnetic) {
269 state.retainedConstructors.push_back(constructor);
270 forManagers([&](G4ProcessManager* manager) {
271 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
272 auto* process = (*manager->GetProcessList())[i];
273 if (!before.contains(process)) { state.retained.insert(process); }
278 state.initialized =
true;
283 activeList->SetCuts();
284 if (productionCut) { SetCutsWithDefault(); }
288 G4VUserPhysicsList::InitializeWorker();
293 for (
auto* constructor : activeConstructors) {
294 if (constructor->GetPhysicsType() != bElectromagnetic) { constructor->TerminateWorker(); }
296 for (
auto* constructor : threadState.Get().retainedConstructors) { constructor->TerminateWorker(); }
297 G4VUserPhysicsList::TerminateWorker();
298 delete workerMessenger;
299 workerMessenger =
nullptr;
302void RuntimePhysicsList::clearProcesses() {
303 G4AutoLock lock(&constructionMutex);
305 const auto& retained = threadState.Get().retained;
306 std::map<G4ProcessManager*, G4ProcessManager*> replacements;
307 forManagers([&](G4ProcessManager* manager) {
310 auto* replacement =
new G4ProcessManager(manager->GetParticleType());
311 replacement->SetVerboseLevel(manager->GetVerboseLevel());
312 replacements.emplace(manager, replacement);
313 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
314 auto* process = (*manager->GetProcessList())[i];
315 if (!retained.contains(process)) {
continue; }
316 const auto rest = manager->GetProcessOrdering(process, idxAtRest);
317 const auto along = manager->GetProcessOrdering(process, idxAlongStep);
318 const auto post = manager->GetProcessOrdering(process, idxPostStep);
319 replacement->AddProcess(process, rest, along, post);
320 for (
const auto index : {idxAtRest, idxAlongStep, idxPostStep}) {
321 if (manager->GetProcessOrdering(process, index) == 0) {
322 replacement->SetProcessOrderingToFirst(process, index);
325 replacement->SetProcessActivation(process, manager->GetProcessActivation(i));
327 for (
int i = manager->GetProcessListLength() - 1; i >= 0; --i) {
328 auto* process = (*manager->GetProcessList())[i];
329 if (!retained.contains(process)) {
332 manager->RemoveProcess(process);
336 std::vector<std::pair<G4ParticleDefinition*, G4ProcessManager*>> particles;
337 auto* iterator = G4ParticleTable::GetParticleTable()->GetIterator();
338 iterator->reset(
false);
339 while ((*iterator)()) {
340 auto* particle = iterator->value();
341 if (
const auto found = replacements.find(particle->GetProcessManager()); found != replacements.end()) {
342 particles.emplace_back(particle, found->second);
345 for (
const auto& [particle, replacement] : particles) {
346 particle->SetProcessManager(replacement);
347 if (!G4Threading::IsWorkerThread()) { particle->SetMasterProcessManager(replacement); }
349 for (
const auto& [oldManager, replacement] : replacements) {
delete oldManager; }
350 for (
auto* process :
processes) {
delete process; }
351 if (G4Threading::IsWorkerThread()) {
352 for (
auto* constructor : activeConstructors) {
353 if (constructor->GetPhysicsType() != bElectromagnetic) { constructor->TerminateWorker(); }
358void RuntimePhysicsList::rebuildWorker() {
359 G4RunManager::GetRunManager()->InitializePhysics();
360 G4RunManager::GetRunManager()->PhysicsHasBeenModified();
363void RuntimePhysicsList::broadcast(
const std::string& command) {
364 if (!messenger) {
return; }
365 const int status = G4UImanager::GetUIpointer()->ApplyCommand(
"/gemc/physics/" + command);
366 if (status) {
throw std::runtime_error(
"Could not apply physics command: " + command); }
367 flushWorkerCommands();
371 if (!editableState(error)) {
return false; }
372 if (!std::isfinite(millimeters) || millimeters <= 0) {
373 error =
"Production cut must be a positive length in millimeters.";
376 if (G4StateManager::GetStateManager()->GetCurrentState() == G4State_Idle) {
378 flushWorkerCommands();
380 productionCut = millimeters * mm;
381 defaultCutValue = *productionCut;
382 if (G4StateManager::GetStateManager()->GetCurrentState() == G4State_Idle) {
383 SetCutsWithDefault();
384 G4RunManager::GetRunManager()->PhysicsHasBeenModified();
390 std::map<std::string, ProcessState> result;
391 forManagers([&](G4ProcessManager* manager) {
392 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
393 auto* process = (*manager->GetProcessList())[i];
394 const auto name = process->GetProcessName();
395 const bool active = manager->GetProcessActivation(i);
396 auto [it, inserted] = result.try_emplace(name,
ProcessState{
397 name, category(process), active,
false,
398 process->GetProcessType() != fTransportation, {}});
399 if (!inserted && active != it->second.active) { it->second.mixed =
true; }
400 it->second.particles.push_back(manager->GetParticleType()->GetParticleName());
403 std::vector<ProcessState> states;
404 for (
auto& [name, state] : result) {
405 std::sort(state.particles.begin(), state.particles.end());
406 state.particles.erase(std::unique(state.particles.begin(), state.particles.end()), state.particles.end());
407 states.push_back(std::move(state));
413 if (!editableState(error)) {
return false; }
415 const auto found = std::find_if(states.begin(), states.end(),
416 [&](
const auto& state) { return state.name == name; });
417 if (found == states.end() || !found->editable) {
418 error =
"Process is unavailable or required for transportation: " + name;
421 activation[name] = enabled;
422 broadcast(
"applyActivation");
423 G4RunManager::GetRunManager()->PhysicsHasBeenModified();
427void RuntimePhysicsList::applyActivation() {
428 forManagers([&](G4ProcessManager* manager) {
429 for (
int i = 0; i < manager->GetProcessListLength(); ++i) {
430 const auto* process = (*manager->GetProcessList())[i];
431 const auto choice = activation.find(process->GetProcessName());
432 if (choice != activation.end() && process->GetProcessType() != fTransportation) {
433 manager->SetProcessActivation(i, choice->second);
void SetNewValue(G4UIcommand *input, G4String) override
GPhysicsMessenger(gphysics::RuntimePhysicsList &input)
void TerminateWorker() override
void InitializeWorker() override
void ConstructProcess() override
RuntimePhysicsList(const std::string &selection)
static std::vector< std::string > referenceLists()
static std::vector< std::string > constructors()
bool setProductionCut(double millimeters, std::string &error)
static RuntimePhysicsList * current()
friend class ::GPhysicsMessenger
bool select(const std::string &selection, std::string &error)
~RuntimePhysicsList() override
std::vector< ProcessState > processes() const
void ConstructParticle() override
bool setProcessEnabled(const std::string &name, bool enabled, std::string &error)
std::string demangle(const char *name)