diff --git a/docker/run_tests.sh b/docker/run_tests.sh index 8362ce4718..05345ad0a7 100755 --- a/docker/run_tests.sh +++ b/docker/run_tests.sh @@ -5,4 +5,4 @@ source /nexus/docker/env.sh source /opt/conda/etc/profile.d/conda.sh conda activate tests -/nexus/bin/nexus-test && pytest -v +/nexus/bin/nexus-test && pytest -m "not slow" -v diff --git a/source/base/NexusExceptionHandler.cc b/source/base/NexusExceptionHandler.cc new file mode 100644 index 0000000000..cb125b096b --- /dev/null +++ b/source/base/NexusExceptionHandler.cc @@ -0,0 +1,237 @@ +// ---------------------------------------------------------------------------- +// nexus | NexusExceptionHandler.cc +// +// This class turns warnings into exceptions, to make the run end. +// It is useful in some tests, or for debugging. +// +// The NEXT Collaboration +// ---------------------------------------------------------------------------- + +#include + +#include "NexusExceptionHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +NexusExceptionHandler::NexusExceptionHandler() {} + +NexusExceptionHandler::~NexusExceptionHandler() {} + +G4bool NexusExceptionHandler::operator==(const NexusExceptionHandler& right) const +{ + return (this == &right); +} + +G4bool NexusExceptionHandler::operator!=(const NexusExceptionHandler& right) const +{ + return (this != &right); +} + +G4bool NexusExceptionHandler::Notify(const char* originOfException, + const char* exceptionCode, + G4ExceptionSeverity severity, + const char* description) +{ + static const G4String es_banner = + "\n-------- EEEE ------- G4Exception-START -------- EEEE -------\n"; + static const G4String ee_banner = + "\n-------- EEEE -------- G4Exception-END --------- EEEE -------\n"; + static const G4String ws_banner = + "\n-------- WWWW ------- G4Exception-START -------- WWWW -------\n"; + static const G4String we_banner = + "\n-------- WWWW -------- G4Exception-END --------- WWWW -------\n"; + std::ostringstream message; + message << "*** G4Exception : " << exceptionCode << G4endl + << " issued by : " << originOfException << G4endl << description + << G4endl; + G4bool abortionForCoreDump = false; + G4ApplicationState aps = G4StateManager::GetStateManager()->GetCurrentState(); + switch(severity) + { + case FatalException: + G4cerr << es_banner << message.str() + << "*** Fatal Exception *** core dump ***" << G4endl; + DumpTrackInfo(); + G4cerr << ee_banner << G4endl; + abortionForCoreDump = true; + break; + case FatalErrorInArgument: + G4cerr << es_banner << message.str() + << "*** Fatal Error In Argument *** core dump ***" << G4endl; + DumpTrackInfo(); + G4cerr << ee_banner << G4endl; + abortionForCoreDump = true; + break; + case RunMustBeAborted: + if(aps == G4State_GeomClosed || aps == G4State_EventProc) + { + G4cerr << es_banner << message.str() << "*** Run Must Be Aborted ***" + << G4endl; + DumpTrackInfo(); + G4cerr << ee_banner << G4endl; + G4RunManager::GetRunManager()->AbortRun(false); + } + abortionForCoreDump = false; + break; + case EventMustBeAborted: + if(aps == G4State_EventProc) + { + G4cerr << es_banner << message.str() << "*** Event Must Be Aborted ***" + << G4endl; + DumpTrackInfo(); + G4cerr << ee_banner << G4endl; + G4RunManager::GetRunManager()->AbortEvent(); + } + abortionForCoreDump = false; + break; + default: + G4cerr << ws_banner << message.str() + << "*** Fatal Exception *** core dump ***" << G4endl; + G4cerr << "*** This is a warning message that has been turned into exception. ***" + << G4endl; + DumpTrackInfo(); + G4cerr << we_banner << G4endl; + abortionForCoreDump = true; + break; + } + return abortionForCoreDump; +} + +void NexusExceptionHandler::DumpTrackInfo() +{ + const G4Track* theTrack = nullptr; + const G4Step* theStep = nullptr; + if (G4StateManager::GetStateManager()->GetCurrentState() == G4State_EventProc) + { + G4SteppingManager* steppingMgr = G4RunManagerKernel::GetRunManagerKernel() + ->GetTrackingManager() + ->GetSteppingManager(); + theTrack = steppingMgr->GetfTrack(); + theStep = steppingMgr->GetfStep(); + } + + if (theTrack == nullptr) + { + G4cerr << " **** Track information is not available at this moment" + << G4endl; + } + else + { + G4cerr << "G4Track (" << theTrack + << ") - track ID = " << theTrack->GetTrackID() + << ", parent ID = " << theTrack->GetParentID() << G4endl; + G4cerr << " Particle type : " + << theTrack->GetParticleDefinition()->GetParticleName(); + if(theTrack->GetCreatorProcess()) + { + G4cerr << " - creator process : " + << theTrack->GetCreatorProcess()->GetProcessName() + << ", creator model : " << theTrack->GetCreatorModelName() + << G4endl; + } + else + { + G4cerr << " - creator process : not available" << G4endl; + } + G4cerr << " Kinetic energy : " + << G4BestUnit(theTrack->GetKineticEnergy(), "Energy") + << " - Momentum direction : " << theTrack->GetMomentumDirection() + << G4endl; + } + + if (theStep == nullptr) + { + G4cerr << " **** Step information is not available at this moment" + << G4endl; + } + else + { + G4cerr << " Step length : " + << G4BestUnit(theStep->GetStepLength(), "Length") + << " - total energy deposit : " + << G4BestUnit(theStep->GetTotalEnergyDeposit(), "Energy") << G4endl; + G4cerr << " Pre-step point : " << theStep->GetPreStepPoint()->GetPosition(); + G4cerr << " - Physical volume : "; + if(theStep->GetPreStepPoint()->GetPhysicalVolume()) + { + G4cerr << theStep->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if(theStep->GetPreStepPoint()->GetMaterial()) + { + G4cerr << " (" << theStep->GetPreStepPoint()->GetMaterial()->GetName() + << ")"; + } + else + { + G4cerr << " (material not available)"; + } + } + else + { + G4cerr << "not available"; + } + G4cerr << G4endl; + if(theStep->GetPreStepPoint()->GetProcessDefinedStep()) + { + G4cerr + << " - defined by : " + << theStep->GetPreStepPoint()->GetProcessDefinedStep()->GetProcessName() + << " - step status : " << theStep->GetPreStepPoint()->GetStepStatus() + << G4endl; + } + else + { + G4cerr << " - defined by : not available" << G4endl; + } + G4cerr << " Post-step point : " + << theStep->GetPostStepPoint()->GetPosition(); + G4cerr << " - Physical volume : "; + if(theStep->GetPostStepPoint()->GetPhysicalVolume()) + { + G4cerr << theStep->GetPostStepPoint()->GetPhysicalVolume()->GetName(); + if(theStep->GetPostStepPoint()->GetMaterial()) + { + G4cerr << " (" << theStep->GetPostStepPoint()->GetMaterial()->GetName() + << ")"; + } + else + { + G4cerr << " (material not available)"; + } + } + else + { + G4cerr << "not available"; + } + G4cerr << G4endl; + if(theStep->GetPostStepPoint()->GetProcessDefinedStep()) + { + G4cerr << " - defined by : " + << theStep->GetPostStepPoint() + ->GetProcessDefinedStep() + ->GetProcessName() + << " - step status : " + << theStep->GetPostStepPoint()->GetStepStatus() << G4endl; + } + else + { + G4cerr << " - defined by : not available" << G4endl; + } + G4cerr << " *** Note: Step information might not be properly updated." + << G4endl; + } +} diff --git a/source/base/NexusExceptionHandler.h b/source/base/NexusExceptionHandler.h new file mode 100644 index 0000000000..a722e76bcd --- /dev/null +++ b/source/base/NexusExceptionHandler.h @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------- +// nexus | NexusExceptionHandler.h +// +// This class turns warnings into exceptions, to make the run end. +// It is useful in some tests, or for debugging. +// +// The NEXT Collaboration +// ---------------------------------------------------------------------------- + +#ifndef NexusExceptionHandler_h +#define NexusExceptionHandler_h + +#include "globals.hh" +#include "G4ExceptionSeverity.hh" +#include "G4VExceptionHandler.hh" + +class NexusExceptionHandler : public G4VExceptionHandler +{ + public: + + NexusExceptionHandler(); + virtual ~NexusExceptionHandler(); + G4bool operator==(const NexusExceptionHandler& right) const; + G4bool operator!=(const NexusExceptionHandler& right) const; + + NexusExceptionHandler(const NexusExceptionHandler&) = delete; + NexusExceptionHandler& operator=(const NexusExceptionHandler&) = delete; + + virtual G4bool Notify(const char* originOfException, + const char* exceptionCode, G4ExceptionSeverity severity, + const char* description); + // Will be invoked by G4StateManager when G4Exception occurs. + // If TRUE returned, core dump is generated, while if FALSE, + // the program execution continues. + + private: + + void DumpTrackInfo(); +}; + +#endif diff --git a/source/nexus.cc b/source/nexus.cc index 8a1d7ea8c3..dd423ae951 100644 --- a/source/nexus.cc +++ b/source/nexus.cc @@ -7,7 +7,9 @@ // ---------------------------------------------------------------------------- #include "NexusApp.h" +#include "NexusExceptionHandler.h" +#include #include #include #include @@ -24,6 +26,7 @@ void PrintUsage() G4cerr << "Available options:" << G4endl; G4cerr << " -b, --batch : Run in batch mode (default)\n" << " -i, --interactive : Run in interactive mode\n" + << " -o, --overlap-check : Turn warnings into exceptions and increase precision in overlap check\n" << " -n, --nevents : Number of events to simulate\n" << " -p, --precision : Number of significant figures in verbosity" << G4endl; @@ -41,6 +44,7 @@ G4int main(int argc, char** argv) if (argc < 2) PrintUsage(); G4bool batch = true; + G4bool overlap_check = false; G4int nevents = 0; G4int precision = -1; @@ -48,6 +52,7 @@ G4int main(int argc, char** argv) { {"batch", no_argument, 0, 'b'}, {"interactive", no_argument, 0, 'i'}, + {"overlaps", no_argument, 0, 'o'}, {"precision", required_argument, 0, 'p'}, {"nevents", required_argument, 0, 'n'}, {0, 0, 0, 0} @@ -59,7 +64,7 @@ G4int main(int argc, char** argv) // int option_index = 0; opterr = 0; - c = getopt_long(argc, argv, "bip:n:", long_options, 0); + c = getopt_long(argc, argv, "biop:n:", long_options, 0); if (c==-1) break; // Exit if we are done reading options @@ -73,6 +78,10 @@ G4int main(int argc, char** argv) batch = false; break; + case 'o': + overlap_check = true; + break; + case 'p': precision = atoi(optarg); break; @@ -107,11 +116,20 @@ G4int main(int argc, char** argv) G4SteppingVerbose::UseBestUnit(precision); + if (overlap_check) { + G4StateManager::GetStateManager()->SetExceptionHandler(new NexusExceptionHandler()); + } + NexusApp* app = new NexusApp(macro_filename); app->Initialize(); G4UImanager* UI = G4UImanager::GetUIpointer(); + if (overlap_check) { + UI->ApplyCommand("/geometry/test/resolution 1000000"); + UI->ApplyCommand("/geometry/test/run"); + } + // if (seed < 0) CLHEP::HepRandom::setTheSeed(time(0)); // else CLHEP::HepRandom::setTheSeed(seed); diff --git a/tests/pytest/macros_test.py b/tests/pytest/macros_test.py index 4162067415..f3413c50da 100644 --- a/tests/pytest/macros_test.py +++ b/tests/pytest/macros_test.py @@ -1,5 +1,7 @@ import pytest +from pytest import mark + import glob import os import subprocess @@ -12,6 +14,21 @@ 2. Examples of config files only are not run, for the time being. """ +@pytest.fixture(scope='module') +def check_list(NEXUSDIR): + + macro_list = [NEXUSDIR + "/macros/DEMOPP_grid.init.mac", + NEXUSDIR + "/macros/DEMOPP_plate.init.mac", + NEXUSDIR + "/macros/NEW.init.mac", + NEXUSDIR + "/macros/NEXT100.init.mac", + NEXUSDIR + "/macros/NEXT100_full.init.mac", + NEXUSDIR + "/macros/NextFlex_fullKr.init.mac", + NEXUSDIR + "/macros/NextTonScale.init.mac", + NEXUSDIR + "/macros/black_box.init.mac"] + + return macro_list + + @pytest.fixture(scope='module') def macro_list(NEXUSDIR): @@ -71,6 +88,18 @@ def copy_and_modify_macro(config_tmpdir, output_tmpdir, init_macro): return cp_init_macro +def execute_overlap_checks(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, check_list): + my_env = os.environ.copy() + + for macro in check_list: + init_macro = copy_and_modify_macro(config_tmpdir, output_tmpdir, macro) + nexus_exe = NEXUSDIR + '/bin/nexus' + command = [nexus_exe, '-b', '-o', init_macro] + with capsys.disabled(): + print(f'Checking {macro}') + p = subprocess.run(command, check=True, env=my_env) + + def execute_example_jobs(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, macro_list): my_env = os.environ.copy() @@ -83,6 +112,15 @@ def execute_example_jobs(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, macro_l p = subprocess.run(command, check=True, env=my_env) +@mark.slow +def test_run_overlap_check(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, check_list): + + with capsys.disabled(): + print('') + print(f'*** Overlap checks ***') + + execute_overlap_checks(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, check_list) + @pytest.mark.order('second_to_last') def test_run_fast_examples(capsys, config_tmpdir, output_tmpdir, NEXUSDIR, macro_list): """Run fast simulation macros"""