Hands-on 4 Scoring Introduction Installation Scoring geometry implementation Scorer definition User defined run class Applying filters Important: Every time you open a new shell you need to set your environment up correctly. Introduction By the end of this Hands-on you should be able to: Implement a spherical parameterised geometry to be used for scoring Construct a sensitive detector (G4MultiFunctionalDetector) and attach scorers Implement a user defined run class Construct scorer filters based on particle type and energy This exercise builds on the HandsOn3 exercise. It is based on an experimental setup developed for bremsstrahlung benchmarking at UCSF. The simplified experiment is made up of the following components: Vacuum beam pipe Titanium beam window Iron evacuation chamber window Silicon monitor Beryllium target Electrons are generated with an energy of 15 MeV in the beam pipe. The electrons interact with the target and generate bremsstrahlung photons. A spherical, parameterised, air filled scoring geometry is also implemented. The number of photons crossing this scoring volume is counted in ten theta bins and ten energy bins. The diagram below demonstrates the simulated experiment. The following files are provided with the exercise: beamTest.cc - main program BeamTestDetectorConstruction – material, geometry , sensitive detector and scorer definitions BeamTestPrimaryGeneratorAction - primary particle generator BeamTestPhysicsList - user defined physics list BeamTestRun - user defined run class BeamTestRunAction - G4UserRunAction class BeamTestScoreParameterisation – scoring volume parameterisation All the geometries except the spherical scoring geometry are implemented. The scoring geometry, sensitive detector, scorers and filters are implemented over the course of the exercise. Installation To start this exercise, unpack HandsOn4.tgz to your working directory. Compile and link it using following commands: $ cd HandsOn4 $ make Once you have successfully compiled and linked the code, try it to see how it runs: $ ./bin/$G4SYSTEM/beamTest run.mac This will produce files named G4Data0.heprep and G4Data1.heprep, which can be viewed by invoking the Wired visualization tool. $ wired G4Data0.heprep You should be able to view the following: You can see that all the components with the exception of the scoring volume have been implemented. Scoring Geometry Implementation The mother scoring volume is defined as a thin air filled sphere. It has an inner radius of 1 m, an outer radius of 1.001 m, and is centered in the world volume. Ten scoring rings are placed within the mother scoring volume. Each ring covers 6 degrees in theta and is constructed of air with an inner radius of 1m and an outer radius of 1.001 m. G4PVPlacement is used to construct the mother sphere, while G4PVParameterised is used to construct BeamTestScoreParameterisation is used for the ring parameterisation. The implementation, along with visualisation attributes is shown below. BeamTestDetectorConstruction.cc ----- snipped ----- #include "G4Tubs.hh" #include "G4VisAttributes.hh" // HandsOn4: Scoring volumes #include "BeamTestScoreParameterisation.hh" #include "G4PVParameterised.hh" #include "G4Sphere.hh" ----- snipped ----- //////////////////////////////////////////////////////////////////////// // HandsOn4: Scoring volumes // Mother sphere G4double innerRadius = 1.0*m; G4double outerRadius = 1.001*m; G4VSolid* sphereSolid = new G4Sphere("Sphere_Solid", // Name innerRadius, // Inner radius outerRadius, // Outer radius 0.*deg, // Starting phi 360.*deg, // Delta phi 0.*deg, // Starting theta 180.*deg); // Delta theta G4LogicalVolume* sphereLogical = new G4LogicalVolume(sphereSolid, air, "Sphere_Logical"); the rings. new G4PVPlacement(0, G4ThreeVector(), sphereLogical, "Sphere_Physical", fpWorldLogical, false, 0); // 10 scoring rings G4double deltaTheta = 6.*deg; G4VSolid* scoreSolid = new G4Sphere("Score_Solid", // Name innerRadius, // Inner radius outerRadius, // Outer radius 0.*deg, // Starting phi 360.*deg, // Delta phi 0.*deg, // Starting theta deltaTheta); // Delta theta G4LogicalVolume* fScoreLogical = new G4LogicalVolume(scoreSolid, air, "scoreLog"); BeamTestScoreParameterisation* param = new BeamTestScoreParameterisation(innerRadius, outerRadius, deltaTheta); new G4PVParameterised("scorePhys", // Name fScoreLogical, // Logical volume sphereLogical, // Mother volume kZAxis, // Axis 10, // Number of replicas param); // Parameterisation ----- snipped ----- // HandsOn4: Scoring volume attributes // Mother sphere G4VisAttributes* sphereAttributes = new G4VisAttributes(G4Colour(1.0,1.0,1.0,0.2)); sphereAttributes->SetVisibility(false); sphereLogical->SetVisAttributes(sphereAttributes); // Ring scoring volumes. Green with transparancy. G4VisAttributes* scoreAttributes = new G4VisAttributes(G4Colour(0.0,1.0,0.0,0.2)); scoreAttributes->SetVisibility(true); fScoreLogical->SetVisAttributes(scoreAttributes); Implement the above and compile, link and run the program. Use Wired to check that all the scoring geometries are now in place. You should be able to see something like: Note that the mother scoring sphere has been made invisible by default. You can change this by checking the Sphere_Physical[0] box. Scorer Definition We are interested in counting the number of photons per unit area which cross the inner surface of each scoring ring. To do this we need to: Create a G4MultiFunctionalDetector detector Register the detector with the sensitive detector manager (G4SDManager) Attach the detector to the parameterised scoring volume Create a primitive scorer for scoring surface current Register the primitive scorer with the detector We will use the G4PSSphereSurfaceCurrent scorer to count the number of photons crossing each scoring ring. Since the ten scoring rings are replicas, we only need to create one scorer. This scorer will automatically record the current as a function of replica copy number. The implementation is shown below. BeamTestDetectorConstruction.cc ----- snipped ----- // HandsOn4: Scoring components #include "G4MultiFunctionalDetector.hh" #include "G4PSSphereSurfaceCurrent.hh" #include "G4SDManager.hh" ----- snipped ----- new G4PVParameterised("scorePhys", // Name fScoreLogical, // Logical volume sphereLogical, // Mother volume kZAxis, // Axis 10, // Number of replicas param); // Parameterisation // HandsOn4: Construct scoring components SetupScoring(fScoreLogical); //////////////////////////////////////////////////////////////////////// // Target (Default parameters for Be ) G4Material* beryllium = G4Material::GetMaterial("Beryllium"); ----- snipped ----- void BeamTestDetectorConstruction::SetupScoring(G4LogicalVolume* scoringVolume) { // HandsOn4: Construct scoring components // Create a new sensitive detector named "MyDetector" G4MultiFunctionalDetector* detector = new G4MultiFunctionalDetector("MyDetector"); // Get pointer to detector manager G4SDManager* manager = G4SDManager::GetSDMpointer(); // Register detector with manager manager->AddNewDetector(detector); // Attach detector to scoring volume scoringVolume->SetSensitiveDetector(detector); // Create a primitive Scorer named myScorer G4PSSphereSurfaceCurrent* scorer = new G4PSSphereSurfaceCurrent("myScorer", fCurrent_In); // Register scorer with detector detector->RegisterPrimitive(scorer); G4cout<<"Created G4MultiFunctionalDetector named " <<detector->GetName()<<", and a G4PSSphereSurfaceCurrent scorer " <<"named "<<scorer->GetName()<<G4endl; } After implementing the above, compile, link and run the program. Check that the sensitive detector and scorer names are printed out, as shown below. Printout showing detector and scorer details ----- snipped ----Material: Vacuum density: 0.010 mg/cm3 temperature: 273.15 K pressure: 0.02 atm RadLength: 36.786 km ---> Element: Nitrogen (N) Z = 7.0 N = 14.0 A = 14.01 g/mole fractionMass: 70.00 % Abundance 72.71 % ---> Element: Oxygen (O) Z = 8.0 N = 16.0 A = 16.00 g/mole fractionMass: 30.00 % Abundance 27.29 % Created G4MultiFunctionalDetector named MyDetector, and a G4PSSphereSurfaceCurrent scorer named myScorer BeamTestPhysicsList::SetCuts:CutLength : 1 mm ----- snipped ----- User Defined Run Class The scorer implemented in the previous section will accumulate data over a single event, but we are more interested in data accumulated over an entire run. Therefore, we need to create our own run class, inheriting from G4Run. This class will be responsible for merging the scorer data for all events. A partially implemented user run class called BeamTestRun is provided with the example. Also included is a BeamTestRunAction class which inherits from G4UserRunAction. BeamTestRunAction is responsible for creating an instance of BeamTestRun, and is an example of one of the optional user action classes. BeamTestRunAction needs to be registered with the run manager in the main program, as shown below. beamTest.cc ----- snipped ----- #include "BeamTestPrimaryGeneratorAction.hh" // HandsOn4: User defined run action #include "BeamTestRunAction.hh" #include "G4RunManager.hh" ----- snipped ----- // User action classes runManager->SetUserAction(new BeamTestPrimaryGeneratorAction()); // HandsOn4: User defined run action runManager->SetUserAction(new BeamTestRunAction); // Initialize G4 kernel After implementing the above, compile, link and run the program. Check that you get printout shown below. Printout showing user defined BeamTestRun class creation Voxelisation: top memory users: Percent Memory ------- -------- Heads Nodes Pointers ------ ------ -------- ---------- Total CPU Volume ---------- 67.84 0k 3 14 36 0.00 World_Logical 32.16 0k 1 7 20 0.00 Sphere_Logical Creating user define run class BeamTestRun Start Run processing. HepRepFile writing to G4Data1.heprep The event by event scorer data merging still needs to be implemented in BeamTestRun. This class has a data member, fMap, which is used to hold the accumulated data. fMap is just a map between hit collection id’s and scorer data (G4THitsMap<G4double>). Merging of scorer data is done in the BeamTestRun::RecordEvent(const G4Event* anEvent) method, and involves: Accessing the hit collection for the event (G4HCofThisEvent object) Using the G4HCofThisEvent object to access the G4THitsMap<double> data for each registered scorer Accumulating the hit map data The implementation is shown below. BeamTestRun.cc ----- snipped ----- void BeamTestRun::RecordEvent(const G4Event* anEvent) { numberOfEvent++; // HandsOn4: Merging event data // Get the hits collection G4HCofThisEvent* eventHitCollection = anEvent->GetHCofThisEvent(); if (!eventHitCollection) return; // Update our private fMap std::map< G4int, G4THitsMap<G4double>* >::iterator iter = fMap.begin(); while (iter != fMap.end()) { G4int id = iter->first; // Get the hit collection corresponding to "id" G4THitsMap<G4double>* eventHitsMap = dynamic_cast< G4THitsMap<G4double>* >(eventHitCollection->GetHC(id)); // Expect this to exist assert (0 != eventHitsMap); // Accumulate event data into our G4THitsMap<G4double> map *(iter->second) += *eventHitsMap; iter++; } } After implementing the above, compile, link and run the program for 1000 events. If you don’t want to visualise the results, disable the visualisation system by adding the command /vis/disable to your macro. This will also speed up the processing. Check that you now get data printed out as shown below. Theta bins corresponding to the ten scoring rings are listed in the first column. The corresponding number of photons per unit area (mm-2) crossing the scoring ring are listed the second column. Printout showing current printout in theta bin Number of Events Processed:1000 events. Theta myScorer 0 0.00227 1 0.00115 2 0.000476 3 0.000327 4 0.000244 5 0.000162 6 0.000111 7 9.89e-05 8 9.78e-05 9 8.34e-05 Graphics systems deleted. Visualization Manager deleting... Applying Filters So far we have only used one scorer to count all tracks crossing each of the scoring rings. It is possible to attach a filter to a scorer, so that only hits passing the filter will be processed. For example, if we are only interested in scoring photons within a given energy range, we would attach a suitably configured G4SDParticleWithEnergyFilter filter to the scorer. If we are interested in scoring a number of different energy ranges we can create multiple scorers, each with its own filter. This is demonstrated below, where ten scorers filtering on photons in 1.2 MeV energy bins are generated and registered with the G4MultiFunctionalDetector. BeamTestDetectorConstruction.cc ----- snipped ----- // HandsOn4: Filter #include "G4SDParticleWithEnergyFilter.hh" ----- snipped ----- void BeamTestDetectorConstruction::SetupScoring(G4LogicalVolume* scoringVolume) { // HandsOn4: Construct scoring components // Create a new sensitive detector named "MyDetector" G4MultiFunctionalDetector* detector = new G4MultiFunctionalDetector("MyDetector"); // Get pointer to detector manager G4SDManager* manager = G4SDManager::GetSDMpointer(); // Register detector with manager manager->AddNewDetector(detector); // Attach detector to scoring volume scoringVolume->SetSensitiveDetector(detector); // HandsOn4: Applying filters to scorers G4int nEnergyBins = 10; G4int i(0); for (i=0; i<nEnergyBins; i++) { char name[10]; std::sprintf(name,"Scorer%i", i); // Create a primitive Scorer G4PSSphereSurfaceCurrent* scorer = new G4PSSphereSurfaceCurrent(name, fCurrent_In); // Create a filter G4SDParticleWithEnergyFilter* filter = new G4SDParticleWithEnergyFilter("Gamma Filter"); // Filter will pass only gammas filter->add("gamma"); // Set energy range of gammas filter will accept G4double minEnergy = i*1.2*MeV; G4double maxEnergy = (i+1)*1.2*MeV; filter->SetKineticEnergy(minEnergy, maxEnergy); // Attach filter to scorer. Scorer will only process hits which pass // the filter scorer->SetFilter(filter); G4cout<<"Created scorer with name "<<name; G4cout<<", covering energy range "<<minEnergy*MeV; G4cout<<" : "<<maxEnergy*MeV<<" MeV"<<G4endl; // Register scorer with detector detector->RegisterPrimitive(scorer); } } After implementing the above, compile, link and run the program for around 10000 events. Check that you see the new scorers registered, and that you see the current printed out for each theta and energy bin, as shown below. Printout showing current in each theta and energy bin ----- snipped ----- ---> Element: Nitrogen (N) Z = 7.0 N = 14.0 A = 14.01 g/mole fractionMass: 70.00 % Abundance fractionMass: 30.00 % Abundance 72.71 % ---> Element: Oxygen (O) Z = 8.0 N = 16.0 A = 16.00 g/mole 27.29 % Created scorer with name Scorer0, covering energy range 0 : 1.2 MeV Created scorer with name Scorer1, covering energy range 1.2 : 2.4 MeV Created scorer with name Scorer2, covering energy range 2.4 : 3.6 MeV Created scorer with name Scorer3, covering energy range 3.6 : 4.8 MeV Created scorer with name Scorer4, covering energy range 4.8 : 6 MeV Created scorer with name Scorer5, covering energy range 6 : 7.2 MeV Created scorer with name Scorer6, covering energy range 7.2 : 8.4 MeV Created scorer with name Scorer7, covering energy range 8.4 : 9.6 MeV Created scorer with name Scorer8, covering energy range 9.6 : 10.8 MeV Created scorer with name Scorer9, covering energy range 10.8 : 12 MeV BeamTestPhysicsList::SetCuts:CutLength : 1 mm ----- snipped ----- Number of Events Processed:10000 events. Theta Scorer0 Scorer1 Scorer2 Scorer3 0 0.0132 0.00378 0.00215 0.00093 0.000581 0.000436 0.000407 0.000378 0.000349 0.000174 1 0.00724 2 Scorer4 Scorer5 Scorer6 Scorer7 Scorer8 Scorer9 0.00145 0.000573 0.000457 0.000185 0.000214 0.000126 0.000156 5.83e-05 1.94e-05 0.0041 0.000693 0.00031 0.00027 0.0002 0.000106 0.000112 4.11e-05 2.35e-05 1.76e-05 1.76e-05 3 0.00272 0.00014 6.36e-05 2.97e-05 2.55e-05 2.55e-05 4.24e-06 8.49e-06 4.24e-06 4 0.00201 0.000191 6.03e-05 2.34e-05 1.34e-05 2.01e-05 0 5 0.00138 0.000131 4.75e-05 1.68e-05 2.79e-06 5.58e-06 0 0 0 0 6 0.00113 8.94e-05 3.14e-05 9.66e-06 4.83e-06 0 0 2.42e-06 0 0 7 0.00101 4.52e-05 6.45e-06 0 0 0 0 2.15e-06 8 0.000927 4.11e-05 3.91e-06 3.91e-06 1.96e-06 1.96e-06 0 0 0 0 9 0.000722 2.72e-05 7.25e-06 1.81e-06 0 0 0 0 4.3e-06 0 0 0 1e-05 3.35e-06 0 Graphics systems deleted. An output file called output.csv (Comma Separated Variables) should also be produced. This can be loaded into for example, OpenOffice or Excel, to make a plot of the data. The plot below shows the data for 104 events plotted for a selection of energy bins. You can download the complete source of this exercise from HandsOn4_complete.tgz Geant4 Tutorial Course Gean4.v8.0p01 May 2006 Jane Tinslay – Heavily based on a tutorial given at a SLAC in March 2006 by Tsukasa Aso