Hi @allison
Sorry for the late reply – I’ve been busy with other topics. First of all, thank you!
What is still not clear to me is that I’m currently using ReinitializeGeometry(true), but the visualizer gets killed. I will attach here my constructor for reference:
LayeredDetectorConstruction.h
#ifndef LAYEREDDETECTORCONSTRUCTION_H
#define LAYEREDDETECTORCONSTRUCTION_H
#include "G4VUserDetectorConstruction.hh"
#include "G4ThreeVector.hh"
#include "globals.hh"
#include <vector>
#include <memory>
class G4VPhysicalVolume;
class G4LogicalVolume;
class UserMaterials;
class ActiveAreaSD;
class LayeredDetectorMessenger;
/// Simple value type describing a detector layer.
struct LayerConfig
{
G4String name;
G4ThreeVector size{0, 0, 0}; // full size (X, Y, Z)
G4String material; // material name (resolved via UserMaterials)
G4bool isSensitive{false};
LayerConfig() = default;
LayerConfig(const G4String &n, const G4ThreeVector &s, const G4String &m, G4bool sens)
: name(n), size(s), material(m), isSensitive(sens) {}
};
/// Builds a stack of rectangular layers inside a bounding “casing” with vacuum world.
/// - World is auto-sized to fit the stack plus margins.
/// - A vacuum bounding box hosts the placed layers.
/// - An aluminum casing surrounds the bounding box (open on top).
class LayeredDetectorConstruction : public G4VUserDetectorConstruction
{
public:
LayeredDetectorConstruction();
~LayeredDetectorConstruction() override;
G4VPhysicalVolume *Construct() override;
void ConstructSDandField() override;
// Configuration API
void AddLayer(const LayerConfig &layer);
void ClearLayers();
const std::vector<LayerConfig> &GetLayers() const noexcept { return fLayers; }
/// Position of the TOP SURFACE of the first layer, in world coordinates.
void SetTopPosition(const G4ThreeVector &pos) noexcept { fTopPosition = pos; }
/// Rebuild geometry after changes (safe to call from UI/messenger).
void UpdateGeometry();
private:
// Debug helpers
void DebugInfoLogicalVolume(G4LogicalVolume *lv) const;
void PrintAllLogicalVolumes() const;
private:
// Non-owning: Geant4 owns created solids/volumes; user owns the materials registry.
std::unique_ptr<UserMaterials> fUserMaterials; // thin wrapper around G4 registries
std::unique_ptr<LayeredDetectorMessenger> fMessenger; // UI command handler
// Geometry description
G4ThreeVector fTopPosition{0, 0, 0};
std::vector<LayerConfig> fLayers;
// Built artifacts (non-owning)
G4LogicalVolume *fBoundingLogic{nullptr};
std::vector<G4LogicalVolume *> fLayerLVs; // layer logicals in placement order
};
#endif
LayeredDetectorConstruction.cpp
#include "detector_construction/LayeredDetectorConstruction.h"
#include "detector_construction/LayeredDetectorMessenger.h"
#include "UserMaterials.h"
#include "ActiveAreaSD.h"
#include "Names.h"
#include "RunMetadata.h"
#include "G4Box.hh"
#include "G4SubtractionSolid.hh"
#include "G4LogicalVolume.hh"
#include "G4PVPlacement.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4PhysicalVolumeStore.hh"
#include "G4NistManager.hh"
#include "G4SDManager.hh"
#include "G4RunManager.hh"
#include "G4UnitsTable.hh"
#include "G4SystemOfUnits.hh"
#include "G4Threading.hh"
#include <algorithm>
#include <utility>
namespace
{
// Utility: validate strictly positive dimensions
inline bool HasPositiveSize(const G4ThreeVector &full)
{
return (full.x() > 0) && (full.y() > 0) && (full.z() > 0);
}
}
LayeredDetectorConstruction::LayeredDetectorConstruction()
: fUserMaterials(std::make_unique<UserMaterials>()),
fMessenger(std::make_unique<LayeredDetectorMessenger>(this))
{
// Default top position can be left at (0,0,0) and set via macro.
AddLayer(LayerConfig("default_layer1", G4ThreeVector(5. * mm, 5. * mm, 1. * mm), "SiliconCarbide", true));
AddLayer(LayerConfig("default_layer2", G4ThreeVector(5. * mm, 5. * mm, 1. * mm), "SiliconCarbide", true));
}
LayeredDetectorConstruction::~LayeredDetectorConstruction() = default;
void LayeredDetectorConstruction::AddLayer(const LayerConfig &layer)
{
// Ignore degenerate layers
if (!HasPositiveSize(layer.size))
{
G4cerr << "[LayeredDetectorConstruction] Ignoring layer '" << layer.name
<< "' with non-positive size." << G4endl;
return;
}
fLayers.push_back(layer);
}
void LayeredDetectorConstruction::ClearLayers()
{
fLayers.clear();
fLayerLVs.clear();
}
G4VPhysicalVolume *LayeredDetectorConstruction::Construct()
{
if (fLayers.empty())
{
G4Exception("LayeredDetectorConstruction::Construct",
"GEOM001", FatalException,
"No layers defined. Use macro commands to add layers.");
}
// --- Compute total envelope needed by the stacked layers
G4double totalZ = 0.;
G4double maxX = 0.;
G4double maxY = 0.;
for (const auto &l : fLayers)
{
totalZ += l.size.z();
maxX = std::max(maxX, l.size.x());
maxY = std::max(maxY, l.size.y());
}
// Margins around the overall assembly
const G4double margin = 1.0 * cm;
// World half-dimensions: add margin and absolute top position components
const G4double worldDimX = std::abs(fTopPosition.x()) + maxX + margin;
const G4double worldDimY = std::abs(fTopPosition.y()) + maxY + margin;
const G4double worldDimZ = std::abs(fTopPosition.z()) + totalZ + margin;
// --- World (vacuum)
auto *solidWorld = new G4Box("solidWorld", worldDimX, worldDimY, worldDimZ);
auto *logicWorld = new G4LogicalVolume(solidWorld, fUserMaterials->GetVacuum(), "logicWorld");
auto *physWorld = new G4PVPlacement(
nullptr, {}, logicWorld, "physWorld", nullptr, false, 0, true);
// --- Bounding box that hosts the layers (vacuum)
const G4ThreeVector boundingSize(maxX, maxY, totalZ);
const G4ThreeVector boundingCenter(fTopPosition.x(),
fTopPosition.y(),
fTopPosition.z() + totalZ / 2.0);
auto *boundingSolid = new G4Box("detectorBoundingBox_solid",
boundingSize.x() / 2.0,
boundingSize.y() / 2.0,
boundingSize.z() / 2.0);
fBoundingLogic = new G4LogicalVolume(
boundingSolid, fUserMaterials->GetVacuum(), "detectorBoundingBox_logic");
// Use a large but unique copy number (998) for ease of debugging
new G4PVPlacement(
nullptr, boundingCenter, fBoundingLogic,
"detectorBoundingBox_phys", logicWorld, false, 998, true);
// --- Aluminum casing (open on top)
const G4double casingThickness = 1.0 * mm;
const G4ThreeVector casingOuterSize =
boundingSize + G4ThreeVector(2 * casingThickness, 2 * casingThickness, casingThickness);
auto *solidCasingOuter = new G4Box("casing_outer",
casingOuterSize.x() / 2.0,
casingOuterSize.y() / 2.0,
casingOuterSize.z() / 2.0);
auto *solidCasingInner = new G4Box("casing_inner",
boundingSize.x() / 2.0,
boundingSize.y() / 2.0,
boundingSize.z() / 2.0);
// Offset inner cavity upward so the top remains open
const G4ThreeVector cavityOffset(0, 0, (casingOuterSize.z() - boundingSize.z()) / 2.0);
auto *solidCasing = new G4SubtractionSolid("casing_solid",
solidCasingOuter,
solidCasingInner,
nullptr,
cavityOffset);
auto *logicCasing = new G4LogicalVolume(
solidCasing, fUserMaterials->GetAluminum(), "detectorCasing_logic");
const G4ThreeVector casingCenter = boundingCenter + G4ThreeVector(0, 0, casingThickness / 2.0);
// Unique copy number (999) for casing
new G4PVPlacement(
nullptr, casingCenter, logicCasing,
"detectorCasing_phys", logicWorld, false, 999, true);
// --- Record high-level metadata (world + detector top position)
{
auto meta = RunMetadata::Instance();
meta->SetWorldSize(G4ThreeVector(worldDimX * 2, worldDimY * 2, worldDimZ * 2)); // convert half-size -> full size
meta->SetDetectorPosition(fTopPosition);
meta->ClearLayers();
}
// --- Create layer logical volumes and place them into the bounding box
fLayerLVs.clear();
fLayerLVs.reserve(fLayers.size());
G4double currentZ = fTopPosition.z();
for (size_t i = 0; i < fLayers.size(); ++i)
{
const auto &layer = fLayers[i];
// Solid / logical
auto *solid = new G4Box(layer.name + "_solid",
layer.size.x() / 2, layer.size.y() / 2, layer.size.z() / 2);
auto *material = fUserMaterials->GetMaterialByName(layer.material);
if (!material)
{
G4Exception("LayeredDetectorConstruction::Construct",
"MAT001", FatalException,
("Unknown material: " + layer.material).c_str());
}
auto *logic = new G4LogicalVolume(solid, material, layer.name + "_logic");
// Compute placement: increment by half thickness, place, then increment again
currentZ += layer.size.z() / 2.0;
const G4ThreeVector globalPos(fTopPosition.x(), fTopPosition.y(), currentZ);
const G4ThreeVector localPos = globalPos - boundingCenter;
// Copy numbers: 1..N for layers (avoid 0 and 998–999 already used)
new G4PVPlacement(nullptr, localPos, logic,
layer.name + "_phys", fBoundingLogic, false,
static_cast<G4int>(i + 1), true);
fLayerLVs.push_back(logic);
currentZ += layer.size.z() / 2.0;
// Metadata per layer
RunMetadata::LayerInfo li;
li.name = layer.name;
li.size = layer.size;
li.position = globalPos; // absolute position of the layer center
li.material = layer.material;
li.isActive = layer.isSensitive;
RunMetadata::Instance()->AddLayer(li);
DebugInfoLogicalVolume(logic);
}
return physWorld;
}
void LayeredDetectorConstruction::ConstructSDandField()
{
auto *sdManager = G4SDManager::GetSDMpointer();
// Create a unique SD name per thread to avoid collisions in MT
const G4String uniqueName =
Names::activeAreaSensitiveDetector + "_thread" + std::to_string(G4Threading::G4GetThreadId());
if (sdManager->FindSensitiveDetector(uniqueName, false))
{
// Already created for this thread
return;
}
auto *sd = new ActiveAreaSD(uniqueName);
sdManager->AddNewDetector(sd);
// Register active/inactive layers, attach SD to each logical
for (size_t i = 0; i < fLayers.size(); ++i)
{
auto *lv = fLayerLVs[i];
const auto isActive = fLayers[i].isSensitive;
sd->RegisterActiveLayer(lv->GetName(), isActive);
SetSensitiveDetector(lv, sd); // attaches even if inactive → SD can gate internally
}
PrintAllLogicalVolumes();
}
void LayeredDetectorConstruction::DebugInfoLogicalVolume(G4LogicalVolume *lv) const
{
if (!lv)
return;
G4cout << "[LayeredDetectorConstruction] LV: " << lv->GetName() << G4endl;
if (auto *solid = dynamic_cast<G4Box *>(lv->GetSolid()))
{
const G4ThreeVector fullSize(2 * solid->GetXHalfLength(),
2 * solid->GetYHalfLength(),
2 * solid->GetZHalfLength());
G4cout << " Size: "
<< G4BestUnit(fullSize.x(), "Length") << " × "
<< G4BestUnit(fullSize.y(), "Length") << " × "
<< G4BestUnit(fullSize.z(), "Length") << G4endl;
}
if (auto *mat = lv->GetMaterial())
{
G4cout << " Material: " << mat->GetName() << G4endl;
}
}
void LayeredDetectorConstruction::UpdateGeometry()
{
// Rebuild geometry; true → destroy old geometry before re-construct
G4RunManager::GetRunManager()->ReinitializeGeometry(true);
}
void LayeredDetectorConstruction::PrintAllLogicalVolumes() const
{
G4cout << "[Debug] List of Logical Volumes:" << G4endl;
auto *store = G4LogicalVolumeStore::GetInstance();
for (auto *lv : *store)
{
const auto *mat = lv->GetMaterial();
G4cout << " - " << lv->GetName();
if (mat)
G4cout << " (" << mat->GetName() << ")";
G4cout << G4endl;
}
}
I’m currently starting the geometry (see constructor above) with 2 default layers, and everything works fine. Once the Qt window is started, I call the following sequence of macro commands:
- Remove the default layers with
ClearLayers().
- Add the layers I want by loading a macro with a series of
AddLayer() commands (3 to 5 layers).
- Update the geometry and visualization with
UpdateGeometry(), which calls ReinitializeGeometry(true).
As soon as I hit this last command, the Qt window closes and I get the following error:
# -----------------------------
# Detector
# -----------------------------
# Polyamide ()
/detector/addLayer kapton_layer 5. mm 5. mm 7. um Kapton false
# Aluminum (Al)
/detector/addLayer aluminum_layer 5 mm 5 mm 1. um Aluminum false
# Nickel Silicide (Ni2Si)
/detector/addLayer nickelsilicide_layer 5 mm 5 mm 0.1 um NickelSilicide false
# Silicon Carbide (SiC)
# p+
/detector/addLayer sic_p_plus 5 mm 5 mm 0.3 um SiliconCarbideDoped(Al_1e18) false
/detector/addLayer sic_active_layer 5 mm 5 mm 100. um SiliconCarbideDoped(N_8e13) true
/detector/addLayer sic_layer_back 5 mm 5 mm 149.7 um SiliconCarbideDoped(N_8e13) false
/detector/update
#### Assemblies, Volumes and Solids Stores are wiped out.
#### Region <DefaultRegionForParallelWorld> is cleared.
/run/reinitializeGeometry
-------- WWWW ------- G4Exception-START -------- WWWW -------
*** G4Exception : modeling0015
issued by : G4PhysicalVolumeModel::Validate
Attempt to validate a volume that is no longer in the physical volume store.
*** This is just a warning message. ***
-------- WWWW -------- G4Exception-END --------- WWWW -------
WARNING: Model "G4PhysicalVolumeModel physWorld:0 BasePath: TOP" is no longer valid - being removed
from scene "scene-1"
/vis/scene/notifyHandlers scene-1
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted (core dumped)
I also include an expanded backtrace for debugging using gdb:
/control/execute /home/michael/Michael/geant4/NeutronSimulation/build/macros/detector/SiC0005_5mm_t250.mac
# -----------------------------
# Detector
# -----------------------------
# Polyamide ()
/detector/addLayer kapton_layer 5. mm 5. mm 7. um Kapton false
# Aluminum (Al)
/detector/addLayer aluminum_layer 5 mm 5 mm 1. um Aluminum false
# Nickel Silicide (Ni2Si)
/detector/addLayer nickelsilicide_layer 5 mm 5 mm 0.1 um NickelSilicide false
# Silicon Carbide (SiC)
# p+
/detector/addLayer sic_p_plus 5 mm 5 mm 0.3 um SiliconCarbideDoped(Al_1e18) false
/detector/addLayer sic_active_layer 5 mm 5 mm 100. um SiliconCarbideDoped(N_8e13) true
/detector/addLayer sic_layer_back 5 mm 5 mm 149.7 um SiliconCarbideDoped(N_8e13) false
/detector/update
#### Assemblies, Volumes and Solids Stores are wiped out.
#### Region <DefaultRegionForParallelWorld> is cleared.
/run/reinitializeGeometry
-------- WWWW ------- G4Exception-START -------- WWWW -------
*** G4Exception : modeling0015
issued by : G4PhysicalVolumeModel::Validate
Attempt to validate a volume that is no longer in the physical volume store.
*** This is just a warning message. ***
-------- WWWW -------- G4Exception-END --------- WWWW -------
WARNING: Model "G4PhysicalVolumeModel physWorld:0 BasePath: TOP" is no longer valid - being removed
from scene "scene-1"
/vis/scene/notifyHandlers scene-1
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Thread 1 "sim" received signal SIGABRT, Aborted.
Download failed: Invalid argument. Continuing without source file ./nptl/./nptl/pthread_kill.c.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
warning: 44 ./nptl/pthread_kill.c: No such file or directory
(gdb) bt
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=<optimized out>, signo=6, signo@entry=<error reading variable: Cannot access memory at address 0x7fffffffb7e8>) at ./nptl/pthread_kill.c:89
#3 0x00007ffff604527e in __GI_raise (sig=<error reading variable: Cannot access memory at address 0x7fffffffb7e8>, sig@entry=6) at ../sysdeps/posix/raise.c:26
#4 0x00007ffff60288ff in __GI_abort () at ./stdlib/abort.c:79
Backtrace stopped: Cannot access memory at address 0x7fffffffb8f8
Thanks a lot for your help, I really appreciate it! 
Best regards,
Michael