Memory Management Issue in GDML File Parsing

Hello Geant4 Community,

I am developing an API that uses Geant4 to run simulations based on GDML files. The API is designed to allow multiple simulations to be run sequentially without restarting the entire application. While the first simulation runs smoothly and completes without any issues, I encounter a problem when initiating a second simulation run through the same API instance.

The specific issue arises during the parsing of the GDML file for the second simulation. Despite successfully completing the first simulation, including the parsing and geometry setup, the second attempt to parse a new GDML file results in a segmentation fault.

Here’s a brief overview of the process:

  • The API receives a GDML file as input and uses Geant4 to set up and run the simulation.

  • After the first simulation completes, the API attempts to clear the previous simulation’s data and load a new GDML file for the next simulation.

  • It is at this point, specifically during the parsing of the new GDML file, that the application encounters a segmentation fault.

I suspect the issue is related to how the Geant4 objects and environment are being cleared or reinitialized between the simulations. I have attempted to manage and delete Geant4-related objects appropriately at the end of each simulation run, but the problem persists.

Here is an excerpt from the code where I think the issue might be:

crow::SimpleApp app;
    CROW_ROUTE(app, "/")
        .methods("POST"_method)
        ([&](const crow::request& req) {
             // Utiliser le corps brut de la requête comme géométrie
            std::string geometry = req.body;

            // Création d'un nom de fichier aléatoire
            std::random_device rd;
            std::mt19937 gen(rd());
            std::uniform_int_distribution<> dis(100000, 999999);
            std::string filename = "geometry_" + std::to_string(dis(gen)) + ".gdml";

            // Écriture dans le fichier temporaire
            std::ofstream out(filename);
            out << geometry;
            out.close();
            // Tentative de lecture du fichier GDML
            try {
              // Nettoyer les matériaux
              // Rebuild or reinitialize the detector construction
              G4Material::GetMaterialTable()->clear();
              
              G4GDMLParser *parser = new G4GDMLParser();
              parser->Clear();
              parser->SetOverlapCheck(true);
              parser->Read(filename);  // THE SEGMENTATION FAULT OCCURED HERE

                // Parse the geometry
                // Set world volume and scoring volume
                G01DetectorConstruction *detectorConstruction = new G01DetectorConstruction(
                    parser->GetWorldVolume());
                detectorConstruction->SetScoringVolume(parser->GetVolume("Tracker"));
                detectorConstruction->SetScoringVolumePosition(parser->GetPosition("Tracker_pos"));
                G4RunManager *runManager = new G4RunManager();
                runManager->SetUserInitialization(detectorConstruction);

                // Physics list
                G4VModularPhysicsList *physicsList = new QBBC;
                physicsList->SetVerboseLevel(0);
                runManager->SetUserInitialization(physicsList);

                // User action initialization
                runManager->SetUserInitialization(new ActionInitialization());

                // Initialize the run manager
                runManager->Initialize();

                // Get the pointer to the User Interface manager
                G4UImanager* UImanager = G4UImanager::GetUIpointer();

                // Set the number of events to simulate
                G4String command = "/run/beamOn 5";
                UImanager->ApplyCommand(command);

                
                  // Retrieve the number of events from the current run
                  G4int nofEvents = runManager->GetCurrentRun()->GetNumberOfEvent();
                  
                  G4cout << G4endl << " The final run consists of " << nofEvents << " events." << G4endl;

                  RunAction* runner = const_cast<RunAction*>(dynamic_cast<const RunAction*>(runManager->GetUserRunAction()));
                  auto averageMaterialDistances = runner->getAverageMaterialDistances();
                  crow::json::wvalue json_response;

                 /// Créer un vecteur JSON pour les distances moyennes des matériaux directement dans l'objet JSON principal
                  crow::json::wvalue& materials_json = json_response["materials"];

                  for (const auto& pair : averageMaterialDistances) {
                      materials_json[pair.first] = pair.second / CLHEP::mm;
                  }
                 
                  json_response["numberOfEvents"] = nofEvents;
                  // Add the number of events in the tracker to the JSON response
                  G4int nofEventsInTracker = runner->numberOfEventsInTracker;
                  json_response["numberOfEventsInTracker"] = nofEventsInTracker;


                  // Convert JSON to string
                  std::string jsonResponseString = json_response.dump();

                  G4cout << "JSON Response: " << jsonResponseString << G4endl;
                  
                  // Clean up 
                  delete runManager;
                  delete parser;


                  // Prepare the response message
                  std::ostringstream responseStream;
                  responseStream << "The final run consists of " << nofEvents << " events.";
                  return crow::response(200, jsonResponseString);

            } catch (const std::exception& e) {
                // Si une erreur se produit, retourner une réponse d'erreur
                return crow::response(500, "Error parsing GDML: " + std::string(e.what()));
            }

            
        });

Here is the output exemple of this code when i make the HTTP request twice :

//First Call
G4GDML: Reading 'geometry_310238.gdml'...
G4GDML: VALIDATION ERROR! ID value 'Phantom' is not unique at line: 219
G4GDML: Reading definitions...
G4GDML: Reading materials...
G4GDML: Reading solids...
G4GDML: Reading structure...
Checking overlaps for volume Tracker_PV:0 (G4Box) ... OK! 
Checking overlaps for volume Cible_PV:0 (G4Box) ... OK! 
Checking overlaps for volume Source_PV:0 (G4Box) ... OK! 
Checking overlaps for volume Tube_PV:0 (G4SubtractionSolid) ... OK! 
Checking overlaps for volume Phantom_PV:0 (G4SubtractionSolid) ... OK! 
Checking overlaps for volume Bones_PV:0 (G4Box) ... OK! 
G4GDML: Reading setup...
G4GDML: Reading 'geometry_310238.gdml' done!
Stripping off GDML names of materials, solids and volumes ...

**************************************************************
 Geant4 version Name: geant4-11-01-patch-01 [MT]   (10-February-2023)
                       Copyright : Geant4 Collaboration
                      References : NIM A 506 (2003), 250-303
                                 : IEEE-TNS 53 (2006), 270-278
                                 : NIM A 835 (2016), 186-225
                             WWW : http://geant4.org/
**************************************************************


// Second Call
G4GDML: Reading 'geometry_839320.gdml'...
G4GDML: VALIDATION ERROR! ID value 'Phantom' is not unique at line: 219
G4GDML: Reading definitions...
G4GDML: Reading materials...
G4GDML: Reading solids...
G4GDML: Reading structure...
Segmentation fault (core dumped)

Initially, I suspected the issue might be related to the handling of the G4GDMLParser , specifically whether it was being properly deleted. However, upon closer examination, it appears that the parser is being correctly disposed of. The destructor of the parser (G4GDMLParser::~G4GDMLParser) seems to be functioning as intended, as shown below:

G4GDMLParser::~G4GDMLParser {
    xercesc::XMLPlatformUtils::Terminate();
    if (!urcode) { delete reader; }
    if (!uwcode) { delete writer; }
    delete messenger;
}

Despite this, the segmentation fault occurs during the parsing phase of the second GDML file. This leads me to believe that the issue may not be with the parser’s destruction per se, but possibly with how the Geant4 environment or objects are managed or reinitialized between simulations.

Thank you in advance for your assistance and expertise.

Best regards,

_Geant4 Version:V11.1.1
_Operating System:Ubuntu 22.04.3 LTS
_CMake Version: 3.25.1