Repository: WenjieWu-Sci/FLArE
Pull request #62: benwilson/FASER2 Geometry Overhaul
Author: login: benw22022
Labels: name: enhancement
description: New feature or request
Created: 2025-03-25T13:45:59Z | Merged: 2025-03-28T11:13:12Z

Update the FASER2 geometry to match the Figures 6 & 7 of the FASER2 Performance Note ([https://cds.cern.ch/record/2927003](https://cds.cern.ch/record/2927003)) 

![image](https://github.com/user-attachments/assets/5259de77-7cf4-49b5-8fc4-b746adba7914)
![image](https://github.com/user-attachments/assets/7399ffe9-9a70-4b00-b9f8-ebca233ae5ad)

Substantial changes have been made to `FASER2DetectorConstruction.cc`, now included are
* A mock veto station consisting 2.5cm of lead sandwiched between two polystyrene sensitive detectors  
* A dedicated decay volume element - this is very useful to allow for easy placement of LLP events from FORESEE (new utility added to `HepMCGenerator.cc`  `"/gen/hepmc/placeInDecayVolume"`)
* The default number of tracking stations is updated to be three upstream and three downstream. The gap between each set of tracking layers and the magnet is configurable
* Some mock volumes for the calorimeters and iron shield have been added. These are just cuboids of material at the moment - more detailed study is required for a full implementation. I think it might be an idea to have these elements disabled by default - for most tasks it is unnecessary to simulate the additional material interactions
* An additional tracking layer is added at the back to serve as a muon detector

I've tried my best to make sure there are no geometry overlaps in the new design

I've also removed the extraneous files from the `HEP_HPC-el9.tar` tarball 

The nominal tracking technology for FASER2 is currently SciFi, not scintillating bars - so I've removed the `/det/faser/trackingNBarsX` and `/det/faser/trackingNBarsY` options

The new geometries for the SAMUARI and Crystal Pulling magnet options are shown below:
![image](https://github.com/user-attachments/assets/dba07275-c875-4afe-a18f-f99bbc143c7e)
![image](https://github.com/user-attachments/assets/bae02c9b-0d0d-4d58-aabb-d82bda306565)

I'm making this a draft PR for the moment since I think there is still I think there is still some polishing to do and macro commands to implement - but I think enough progress has been made that the review process can be started

I'd also be keen to get some feedback, particularly @OlivierSalin if you can check that I've got the all the dimensions & parameters correct? I've guesstimated some parameters like the size of the calorimeters and the thickness of the veto shield for example since I'm not sure that these are documented anywhere



Diff:
diff --git a/HEP_HPC-el9.tar b/HEP_HPC-el9.tar
index f6633f2..eef23e3 100644
Binary files a/HEP_HPC-el9.tar and b/HEP_HPC-el9.tar differ
diff --git a/README.md b/README.md
index a15709f..11d87dd 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ cd el9-cvmfs-docker/
 
 This will automatically pull and start the container and copy the `.bashrc` file which mounts `cvmfs` when the container starts.
 
-As mentioned, this software relies upon the `HEP_HPC` library. A precompiled version of this code which is compatible with the container is provided in the repository. To set this up run
+As mentioned, this software relies upon the `HEP_HPC` library. A precompiled version of this code is provided in the repository. To set this up run
 
 ```bash
 # You only need to do this once
@@ -49,8 +49,8 @@ Then to set up the environment do
 source FLArE/local_setup.sh
 ```
 
-
 ## To compile the code
+
 * Create a new directory to contain the executables (assume the path is `/path/to/build`).
 * Assume the path to the source code is `/path/to/source`.
 * To compile, you need to go to the build directory `cd /path/to/build`.
@@ -109,7 +109,6 @@ An example macro for running over this file can be found in `macros/dark_photon_
 
 Older versions of FORESEE output events in the HepMC2 format. To run over HepMC2 files set the option `/hepmc/useHepMC2 true` in your macro. An example macro can be found in `macros/dark_photon_hepmc2.mac`.
 
-
 ## Macro commands
 
 ### Geometry
@@ -163,10 +162,9 @@ Older versions of FORESEE output events in the HepMC2 format. To run over HepMC2
 |/det/faser/magnetLengthZ     | option for CrystalPulling magnet size along Z axis                                   |`1.25 m`|
 |/det/faser/magnetGap         | option for gap length (along Z) between CrystalPullin magnets                        |`0.5 m`|
 |/det/faser/trackingNumber    | option for number of FASER2 tracking stations in each assembly                       |`6`|
-|/det/faser/trackingNBarsX    | option for number of vertical scintillator bars (segmentation along X axis)          |`7`|  
-|/det/faser/trackingNBarsY    | option for number of horizontal scintillator bars (segmentation along Y axis)        |`3`|
 |/det/faser/trackingScinThick | option for scintillator bar thickness (along Z) in tracking layers                   |`1.0 cm`|
-|/det/faser/trackingGap       | option for gap length (along Z) between tracking stations, and gap to magnet         |` 0.5 m`|
+|/det/faser/trackingGap       | option for gap length (along Z) between tracking stations, and gap to magnet         |`0.5 m`|
+|/det/faser/fillCaloAndWall| option to fill FASER2 iron wall and calorimeter placeholder volumes with metal if true. Otherwise, fill with air to save having to compute the additional material interactions. |`true`|
 
 ### Event generators
 
@@ -181,6 +179,7 @@ Older versions of FORESEE output events in the HepMC2 format. To run over HepMC2
 |/gen/hepmc/useHepMC2     | if `hepmc` is selected, set `true` to expect HepMC2 file format (instead of HepMC3)|
 |/gen/hepmc/hepmcInput    | if `hepmc` is selected, give which `.hepmc` file to read events from|
 |/gen/hepmc/vtxOffset     | if `hepmc` is selected, give an x, y, z offset to each vertex. Useful if you need to align events from another generator (i.e. FORESEE) with this G4 simulation.|
+|/gen/hepmc/placeInDecayVolume | if `hepmc` is selected, will automatically translate vertex into the FASER2 decay volume. Assumes that the vertices start from (0,0,0). If this is not the case then `/gen/hepmc/vtxOffset` must also be set.|
 
 ### Analysis
 
@@ -203,5 +202,6 @@ There is [this tutorial](https://conferences.fnal.gov/g4tutorial/g4cd/Documentat
 
 ## Some initial results
 
-[cern indico 1](https://indico.cern.ch/event/1095064/contributions/4621162/attachments/2349156/4006611/20211118%40FLArEDetectorSimulation.pdf) 
+[cern indico 1](https://indico.cern.ch/event/1095064/contributions/4621162/attachments/2349156/4006611/20211118%40FLArEDetectorSimulation.pdf)
+ 
 [cern indico 2](https://indico.cern.ch/event/1250086/#3-status-update-on-flare-simul)
diff --git a/include/DetectorConstruction.hh b/include/DetectorConstruction.hh
index 581f00a..13812da 100644
--- a/include/DetectorConstruction.hh
+++ b/include/DetectorConstruction.hh
@@ -7,6 +7,7 @@
 #include <G4FieldManager.hh>
 #include <G4AssemblyVolume.hh>
 #include "fields/BabyMINDMagneticField.hh"
+#include <vector>
 
 class G4LogicalVolume;
 class DetectorConstructionMessenger;
@@ -71,8 +72,9 @@ class DetectorConstruction : public G4VUserDetectorConstruction {
 
     // FASER2
     G4LogicalVolume* FASER2MagnetLogical;
-    G4LogicalVolume* TrackingVerScinBarLogical;
-    G4LogicalVolume* TrackingHorScinBarLogical; 
+    std::vector<G4LogicalVolume*> FASER2TrackingLogical;
+    G4LogicalVolume* FASER2VetoLogical;
+    G4LogicalVolume* FASER2MuonLogical;
 
     //FASER2nu
     G4LogicalVolume* FASERnu2EmulsionLogical;
diff --git a/include/DetectorConstructionMessenger.hh b/include/DetectorConstructionMessenger.hh
index 08a3cd0..0cc0930 100644
--- a/include/DetectorConstructionMessenger.hh
+++ b/include/DetectorConstructionMessenger.hh
@@ -101,10 +101,9 @@ class DetectorConstructionMessenger: public G4UImessenger {
     G4UIcmdWithAnInteger* faserMagnetNumberCmd;
     // Tracking stations
     G4UIcmdWithAnInteger* faserTrackingNumberCmd;
-    G4UIcmdWithAnInteger* faserTrackingNBarsYCmd;
-    G4UIcmdWithAnInteger* faserTrackingNBarsXCmd;
     G4UIcmdWithADoubleAndUnit* faserTrackingScinThickCmd;
     G4UIcmdWithADoubleAndUnit* faserTrackingGapCmd;
+    G4UIcmdWithABool* faserFillCaloAndWallCmd;
 };
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
diff --git a/include/generators/HepMCGenerator.hh b/include/generators/HepMCGenerator.hh
index 9bf0d29..4dfd782 100644
--- a/include/generators/HepMCGenerator.hh
+++ b/include/generators/HepMCGenerator.hh
@@ -25,11 +25,13 @@ class HepMCGenerator : public GeneratorBase
     void SetHepMCFilename(G4String val) { fHepMCFilename = val; }
     void SetUseHepMC2(G4bool val) { fUseHepMC2 = val; }
     void SetHepMCVertexOffset(G4ThreeVector val) { fVtxOffset = val; }
+    void SetPlaceInDecayVolume(G4bool val) { fPlaceInDecayVolume = val; }    
 
   private:
 
     G4String fHepMCFilename;
     G4bool fUseHepMC2;
+    G4bool fPlaceInDecayVolume;
     G4ThreeVector fVtxOffset;
     HepMC3::Reader* fAsciiInput;
         
@@ -37,6 +39,7 @@ class HepMCGenerator : public GeneratorBase
     std::shared_ptr<HepMC3::GenEvent> GenerateHepMCEvent();
     G4bool CheckVertexInsideWorld (const G4ThreeVector& pos) const;
     void HepMC2G4(const std::shared_ptr<HepMC3::GenEvent> hepmcevt, G4Event* g4event);
+    G4double GetStartOfDecayVolume();
         
 };
 
diff --git a/include/generators/HepMCGeneratorMessenger.hh b/include/generators/HepMCGeneratorMessenger.hh
index 1aaa406..937b0df 100644
--- a/include/generators/HepMCGeneratorMessenger.hh
+++ b/include/generators/HepMCGeneratorMessenger.hh
@@ -29,6 +29,7 @@ class HepMCGeneratorMessenger: public G4UImessenger
     G4UIcmdWithAString* fHepMCInputFileCmd;
     G4UIcmdWith3VectorAndUnit* fHepMCVertexOffsetCmd;
     G4UIcmdWithABool* fUseHepMC2Cmd;
+    G4UIcmdWithABool* fHepMCPlaceInDecayVolumeCmd;
 
 };
 
diff --git a/include/geometry/FASER2DetectorConstruction.hh b/include/geometry/FASER2DetectorConstruction.hh
index 65b479e..b127050 100644
--- a/include/geometry/FASER2DetectorConstruction.hh
+++ b/include/geometry/FASER2DetectorConstruction.hh
@@ -3,6 +3,7 @@
 
 #include "G4LogicalVolume.hh"
 #include "G4AssemblyVolume.hh"
+#include <vector>
 
 #include "DetectorConstructionMaterial.hh"
 
@@ -20,23 +21,41 @@ class FASER2DetectorConstruction {
   
     // Returns logical volumes
     G4LogicalVolume* GetMagneticVolume(){ return fMagnetWindow;}
-    G4LogicalVolume* GetHorTrackingScinBar(){ return fHorTrackingScinBar;}
-    G4LogicalVolume* GetVerTrackingScinBar(){ return fVerTrackingScinBar;}
+    G4LogicalVolume* GetTrackingStation(){ return fTrackingLogical;}
+    G4LogicalVolume* GetVetoStations(){ return fVetoScinLogical;}
+    G4LogicalVolume* GetMuonStations(){ return fMuonDetLogical;}
+    std::vector<G4LogicalVolume*> GetTrackingStations(){ return fTrackingStationsLogical;}
 
+  private:
+    
     // Construction methods
     void BuildSAMURAIDesign();
     void BuildCrystalPullingDesign();
-    void BuildTrackingStation();
-
-  private:
+    void SetVisualisation();
 
+    // Logical volumes and material
     G4LogicalVolume* fFASER2Assembly;
     DetectorConstructionMaterial* fMaterials;
     G4LogicalVolume* fMagnetYoke;
     G4LogicalVolume* fMagnetWindow;
+    G4LogicalVolume* fTrackingLogical;
+    G4LogicalVolume* fVetoScinLogical;
+    G4LogicalVolume* fVetoShieldLogical;
+    G4LogicalVolume* fDecayVolumeLogical;
+    std::vector<G4LogicalVolume*> fTrackingStationsLogical;
     G4AssemblyVolume* fTrackingStation;
-    G4LogicalVolume* fHorTrackingScinBar;   
-    G4LogicalVolume* fVerTrackingScinBar;   
+    G4LogicalVolume* fEMCalLogical;
+    G4LogicalVolume* fHadCalLogical;
+    G4LogicalVolume* fIronWallLogical;
+    G4LogicalVolume* fMuonDetLogical;
+
+    // Veto
+    G4double fVetoLengthX;
+    G4double fVetoLengthY;
+    G4double fVetoShieldThickness;
+
+    // Decay volume
+    G4double fDecayVolumeLength;
 
     // SAMURAI design
     G4double fMagnetWindowX;
@@ -57,9 +76,14 @@ class FASER2DetectorConstruction {
     G4double fTrackingStationX;
     G4double fTrackingStationY;
     G4double fScinThickness;
-    G4int fNScinBarsY;
-    G4int fNScinBarsX;
     G4double fTrackingStationGap;
+    G4double fUpstreamTrackingStationGap;
+    G4double fDownstreamTrackingStationGap;
+
+    // Calorimeter guess??
+    G4double fEMCaloThickness;
+    G4double fHadCaloThickness;
+    G4double fIronWallThickness;
 };
 
 #endif
diff --git a/include/geometry/GeometricalParameters.hh b/include/geometry/GeometricalParameters.hh
index 4631944..db30345 100644
--- a/include/geometry/GeometricalParameters.hh
+++ b/include/geometry/GeometricalParameters.hh
@@ -105,6 +105,12 @@ class GeometricalParameters  {
     G4double GetMagnetZPosition() { return fMagnetZPos; }
     void SetFASER2TotalSizeZ(G4double val) { fFASER2TotalSizeZ = val; }
     G4double GetFASER2TotalSizeZ() { return fFASER2TotalSizeZ; }
+    void SetfFASER2DecayVolumeLength (G4double val) { fFASER2DecayVolumeLength = val;}
+    G4double GetFASER2DecayVolumeLength() { return fFASER2DecayVolumeLength; }
+    void SetFASER2TrackerX(G4double val) { fFASER2TrackerX = val; }
+    G4double GetFASER2TrackerX() { return fFASER2TrackerX; }
+    void SetFASER2TrackerY(G4double val) { fFASER2TrackerY = val; }
+    G4double GetFASER2TrackerY() { return fFASER2TrackerY; }
     // SAMURAI design
     void SetFASER2MagnetWindowX(G4double val) { fFASER2MagnetWindowX = val; }
     G4double GetFASER2MagnetWindowX() { return fFASER2MagnetWindowX; }
@@ -121,8 +127,6 @@ class GeometricalParameters  {
     G4double GetFASER2MagnetInnerR() { return fFASER2MagnetInnerR; }
     void SetFASER2MagnetOuterR(G4double val) { fFASER2MagnetOuterR = val; }
     G4double GetFASER2MagnetOuterR() { return fFASER2MagnetOuterR; }
-    void SetFASER2MagnetLengthZ(G4double val) { fFASER2MagnetLengthZ = val; }
-    G4double GetFASER2MagnetLengthZ() { return fFASER2MagnetLengthZ; }
     void SetNFASER2Magnets(G4int val) { fNFASER2Magnets = val; }
     G4int GetNFASER2Magnets() { return fNFASER2Magnets; }
     void SetFASER2MagnetGap(G4double val) { fFASER2MagnetGap = val; }
@@ -132,15 +136,30 @@ class GeometricalParameters  {
     // Tracking station
     void SetNTrackingStations(G4int val) { fNTrackingStations = val; }
     G4int GetNTrackingStations() { return fNTrackingStations; }  
-    void SetNScintillatorBarsY(G4int val) { fNScintillatorBarsY = val; }
-    G4int GetNScintillatorBarsY() { return fNScintillatorBarsY; }  
-    void SetNScintillatorBarsX(G4int val) { fNScintillatorBarsX = val; }
-    G4int GetNScintillatorBarsX() { return fNScintillatorBarsX; }  
     void SetScintillatorThickness(G4double val) { fScintillatorThickness = val; }
     G4double GetScintillatorThickness() { return fScintillatorThickness; }  
     void SetTrackingStationGap(G4double val) { fTrackingStationGap = val; }
     G4double GetTrackingStationGap() { return fTrackingStationGap; }  
+    G4double GetFASER2UpstreamTrackingStationGap() { return fFASER2UpstreamTrackingStationGap; }
+    G4double GetFASER2DownstreamTrackingStationGap() { return fFASER2DownstreamTrackingStationGap; }
+    G4double GetFASER2EMCaloThickness() { return fFASER2EMCaloThickness; }
+    G4double GetFASER2HadCaloThickness() { return fFASER2HadCaloThickness; }
+    G4double GetFASER2IronWallThickness() { return fFASER2IronWallThickness; }
+    G4double GetFASER2VetoLengthX() { return fFASER2VetoLengthX; }
+    G4double GetFASER2VetoLengthY() { return fFASER2VetoLengthY; }
+    G4double GetFASER2VetoShieldThickness() { return fFASER2VetoShieldThickness; }
+    void SetFASER2UpstreamTrackingStationGap(G4double val) { fFASER2UpstreamTrackingStationGap = val; }
+    void SetFASER2DownstreamTrackingStationGap(G4double val) { fFASER2DownstreamTrackingStationGap = val; }
+    void SetFASER2EMCaloThickness(G4double val) { fFASER2EMCaloThickness = val; }
+    void SetFASER2HadCaloThickness(G4double val) { fFASER2HadCaloThickness = val; }
+    void SetFASER2IronWallThickness(G4double val) { fFASER2IronWallThickness = val; }
+    void SetFASER2VetoLengthX(G4double val) { fFASER2VetoLengthX = val; }
+    void SetFASER2VetoLengthY(G4double val) { fFASER2VetoLengthY = val; }
+    void SetFASER2VetoShieldThickness(G4double val) { fFASER2VetoShieldThickness = val; }
     
+    void SetFillCaloAndWallVolumes(G4bool val) {fFASER2FillCaloAndWall = val;}
+    G4bool GetFillCaloAndWallVolumes() {return fFASER2FillCaloAndWall;}
+
     G4ThreeVector GetFASER2Position() { return fFASER2Pos; }
     void SetFASER2Position(G4ThreeVector val) { fFASER2Pos = val; }
 
@@ -246,6 +265,8 @@ class GeometricalParameters  {
     G4double fTrackingStationTotalSizeZ;
     G4double fMagnetZPos;
     G4double fFASER2TotalSizeZ;
+    G4double fFASER2DecayVolumeLength;
+    
     // SAMURAI design
     G4double fFASER2MagnetWindowX;
     G4double fFASER2MagnetWindowY;
@@ -255,17 +276,27 @@ class GeometricalParameters  {
     // Crystal-Pulling design
     G4double fFASER2MagnetInnerR;
     G4double fFASER2MagnetOuterR;
-    G4double fFASER2MagnetLengthZ;
     G4int fNFASER2Magnets;
     G4double fFASER2MagnetGap;
     G4double fFASER2MagnetSpacing;
     // Tracking stations
     G4int fNTrackingStations;
-    G4int fNScintillatorBarsY;
-    G4int fNScintillatorBarsX;
+    G4double fFASER2TrackerX;
+    G4double fFASER2TrackerY;
     G4double fScintillatorThickness;
     G4double fTrackingStationGap;
     G4ThreeVector fFASER2Pos;
+    G4double fFASER2UpstreamTrackingStationGap;
+    G4double fFASER2DownstreamTrackingStationGap;
+    G4double fFASER2EMCaloThickness;
+    G4double fFASER2HadCaloThickness;
+    G4double fFASER2IronWallThickness;
+    G4double fFASER2VetoLengthX;
+    G4double fFASER2VetoLengthY;
+    G4double fFASER2VetoShieldThickness;
+    
+    G4bool fFASER2FillCaloAndWall; // if true then fill volumes with material, otherwise use air to save CPU
+
 
     // FASERnu2 Emulsion detector
     G4double fFASERnu2TotalSizeZ;
diff --git a/macros/dark_photon_hepmc.mac b/macros/dark_photon_hepmc.mac
index 6be5661..a2690c0 100644
--- a/macros/dark_photon_hepmc.mac
+++ b/macros/dark_photon_hepmc.mac
@@ -6,7 +6,8 @@
 # define hepmc input options
 /gen/select hepmc
 /gen/hepmc/hepmcInput /afs/cern.ch/user/b/bewilson/work/public/hepmc/events_14TeV_m0.3548GeV_c1e-06_to_mu_mu_s1.hepmc3
-/gen/hepmc/vtxOffset 0 0 -12050 mm
+/gen/hepmc/vtxOffset 0 0 1500 mm
+/gen/hepmc/placeInDecayVolume true
 
 # define output options
 /histo/save3DEvd false
diff --git a/macros/dark_photon_hepmc2.mac b/macros/dark_photon_hepmc2.mac
index cabb996..42b5ce3 100644
--- a/macros/dark_photon_hepmc2.mac
+++ b/macros/dark_photon_hepmc2.mac
@@ -7,7 +7,8 @@
 /gen/select hepmc
 /gen/hepmc/useHepMC2 true
 /gen/hepmc/hepmcInput /afs/cern.ch/user/b/bewilson/work/public/hepmc/events_14TeV_m0.3548GeV_c1e-06_to_mu_mu_s1.hepmc2
-/gen/hepmc/vtxOffset 0 0 -12050 m
+/gen/hepmc/vtxOffset 0 0 1500 mm
+/gen/placeInDecayVolume true
 
 # define output options
 /histo/save3DEvd false
diff --git a/macros/geometry_options/FASER2_CP_only.mac b/macros/geometry_options/FASER2_CP_only.mac
new file mode 100644
index 0000000..a0cb462
--- /dev/null
+++ b/macros/geometry_options/FASER2_CP_only.mac
@@ -0,0 +1,41 @@
+# This file defines a simplified configuration for the FPF reference hall
+# The geometry contains:
+# - FASER2 (SAMURAI magnet)
+
+/det/fileGdml FASER2_only.gdml
+
+# Configuring FLArE
+/det/addFLArE false
+/det/flare/addFLArEPos 0 0 4300 mm
+/det/flare/material LAr
+/det/flare/module 3x7
+/det/flare/field 1. tesla
+/det/flare/useBabyMIND false
+
+# Configuring FORMOSA
+# assuming 1.2m gap from FLArE
+/det/addFORMOSA false
+/det/formosa/addFORMOSAPos 0 0 13870 mm
+
+# Configuring FASERnu2
+# assuming 1.2m gap from FORMOSA
+/det/addFASERnu2 false
+/det/fasernu/addFASERnu2Pos 0 0 22023 mm
+
+# Configuring FASER2
+# assuming 1.2m gap from FASERnu2
+# (which include veto stations)
+# + 10m decay tunnel
+/det/addFASER2 true
+/det/faser/addFASER2Pos 0 0 36031 mm
+/det/faser/magnetGeom CrystalPulling
+/det/faser/magnetField 0.6 tesla
+/det/faser/magnetInnerR 0.8 m
+/det/faser/magnetOuterR 1.2 m
+/det/faser/magnetWinZ 1.25 m
+/det/faser/magnetNumber 3
+/det/faser/magnetGap 0.25 m
+/det/faser/trackingNumber 3
+/det/faser/trackingGap 0.5 m
+/det/faser/trackingScinThick 1.0 cm
+
diff --git a/macros/geometry_options/FASER2_only.mac b/macros/geometry_options/FASER2_only.mac
index c33ff82..3a66487 100644
--- a/macros/geometry_options/FASER2_only.mac
+++ b/macros/geometry_options/FASER2_only.mac
@@ -34,9 +34,7 @@
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
diff --git a/macros/geometry_options/FLArE_FASER2_only.mac b/macros/geometry_options/FLArE_FASER2_only.mac
index c35563e..9695152 100644
--- a/macros/geometry_options/FLArE_FASER2_only.mac
+++ b/macros/geometry_options/FLArE_FASER2_only.mac
@@ -35,9 +35,7 @@
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
diff --git a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast.mac b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast.mac
index 40fd52a..612a9fc 100644
--- a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast.mac
+++ b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast.mac
@@ -26,21 +26,19 @@
 # (which include veto stations)
 # + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 36116 mm
+/det/faser/addFASER2Pos 0 0 33576 mm
 /det/faser/magnetGeom SAMURAI
 /det/faser/magnetField 1. tesla
 /det/faser/magnetWinX 3.0 m
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming 1.2m gap from FASER2
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 0 45096 mm
+/det/formosa/addFORMOSAPos 0 0 51531 mm
diff --git a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_BabyMIND.mac b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_BabyMIND.mac
index 9016075..37189f7 100644
--- a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_BabyMIND.mac
+++ b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_BabyMIND.mac
@@ -24,23 +24,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 38006 mm
+/det/faser/addFASER2Pos 0 0 36981 mm
 /det/faser/magnetGeom SAMURAI
 /det/faser/magnetField 1. tesla
 /det/faser/magnetWinX 3.0 m
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming 1.2m gap from FASER2
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 0 46986 mm
+/det/formosa/addFORMOSAPos 0 0 54936 mm
diff --git a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP.mac b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP.mac
index f2f05ed..62c6b68 100644
--- a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP.mac
+++ b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP.mac
@@ -25,23 +25,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 34141 mm
+/det/faser/addFASER2Pos 0 0 35216 mm
 /det/faser/magnetGeom CrystalPulling
 /det/faser/magnetField 0.6 tesla
 /det/faser/magnetInnerR 0.8 m
 /det/faser/magnetOuterR 1.2 m
-/det/faser/magnetLengthZ 1.25 m
+/det/faser/magnetWinZ 1.25 m
 /det/faser/magnetNumber 3
 /det/faser/magnetGap 0.25 m
 /det/faser/trackingNumber 3
-/det/faser/trackingGap 0.1 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
+/det/faser/trackingGap 0.5 m
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming 1.2m gap from FASER2
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 0 41146 mm
+/det/formosa/addFORMOSAPos 0 0 53276 mm
diff --git a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP_BabyMIND.mac b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP_BabyMIND.mac
index eb4edba..cd2ccc9 100644
--- a/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP_BabyMIND.mac
+++ b/macros/geometry_options/FPF_hall_Option1a_FORMOSAlast_CP_BabyMIND.mac
@@ -25,23 +25,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 36031 mm
+/det/faser/addFASER2Pos 0 0 37106 mm
 /det/faser/magnetGeom CrystalPulling
 /det/faser/magnetField 0.6 tesla
 /det/faser/magnetInnerR 0.8 m
 /det/faser/magnetOuterR 1.2 m
-/det/faser/magnetLengthZ 1.25 m
+/det/faser/magnetWinZ 1.25 m
 /det/faser/magnetNumber 3
 /det/faser/magnetGap 0.25 m
 /det/faser/trackingNumber 3
-/det/faser/trackingGap 0.1 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
+/det/faser/trackingGap 0.5 m
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming 1.2m gap from FASER2
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 0 43046 mm
+/det/formosa/addFORMOSAPos 0 0 55186 mm
diff --git a/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder.mac b/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder.mac
index d0c094d..92c4edd 100644
--- a/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder.mac
+++ b/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder.mac
@@ -24,23 +24,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 36116 mm
+/det/faser/addFASER2Pos 0 0 35091 mm
 /det/faser/magnetGeom SAMURAI
 /det/faser/magnetField 1. tesla
 /det/faser/magnetWinX 3.0 m
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming in the middle of decay tunnel
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 -2500 25996 mm
+/det/formosa/addFORMOSAPos 0 -2500 26156 mm
diff --git a/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder_CP.mac b/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder_CP.mac
index a1b0fd2..b2abcc6 100644
--- a/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder_CP.mac
+++ b/macros/geometry_options/FPF_hall_Option1b_FORMOSAunder_CP.mac
@@ -27,21 +27,19 @@
 # (which include veto stations)
 # + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 34141 mm
+/det/faser/addFASER2Pos 0 0 35216 mm
 /det/faser/magnetGeom CrystalPulling
-/det/faser/magnetField 0.6. tesla
+/det/faser/magnetField 0.6 tesla
 /det/faser/magnetInnerR 0.8 m
 /det/faser/magnetOuterR 1.2 m
-/det/faser/magnetLengthZ 1.25 m
+/det/faser/magnetWinZ 1.25 m
 /det/faser/magnetNumber 3
 /det/faser/magnetGap 0.25 m
 /det/faser/trackingNumber 3
-/det/faser/trackingGap 0.1 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
+/det/faser/trackingGap 0.5 m
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming in the middle of decay tunnel
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 -2500 25996 mm
+/det/formosa/addFORMOSAPos 0 -2500 26156 mm
diff --git a/macros/geometry_options/FPF_hall_Option2_FASERnufirst.mac b/macros/geometry_options/FPF_hall_Option2_FASERnufirst.mac
index b4a0d36..1493420 100644
--- a/macros/geometry_options/FPF_hall_Option2_FASERnufirst.mac
+++ b/macros/geometry_options/FPF_hall_Option2_FASERnufirst.mac
@@ -25,23 +25,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FLArE
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 36116 mm
+/det/faser/addFASER2Pos 0 0 35091 mm
 /det/faser/magnetGeom SAMURAI
 /det/faser/magnetField 1. tesla
 /det/faser/magnetWinX 3.0 m
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming in the middle of decay tunnel
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 -2500 25996 mm
+/det/formosa/addFORMOSAPos 0 -2500 27059 mm
diff --git a/macros/geometry_options/FPF_hall_Option2_FASERnufirst_CP.mac b/macros/geometry_options/FPF_hall_Option2_FASERnufirst_CP.mac
index 15f1d11..86e430a 100644
--- a/macros/geometry_options/FPF_hall_Option2_FASERnufirst_CP.mac
+++ b/macros/geometry_options/FPF_hall_Option2_FASERnufirst_CP.mac
@@ -26,23 +26,20 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 34141 mm
+/det/faser/addFASER2Pos 0 0 35216 mm
 /det/faser/magnetGeom CrystalPulling
-/det/faser/magnetField 0.6. tesla
+/det/faser/magnetField 0.6 tesla
 /det/faser/magnetInnerR 0.8 m
 /det/faser/magnetOuterR 1.2 m
-/det/faser/magnetLengthZ 1.25 m
+/det/faser/magnetWinZ 1.25 m
 /det/faser/magnetNumber 3
 /det/faser/magnetGap 0.25 m
 /det/faser/trackingNumber 3
-/det/faser/trackingGap 0.1 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
+/det/faser/trackingGap 0.5 m
 /det/faser/trackingScinThick 1.0 cm
 
 # Configuring FORMOSA
 # assuming in the middle of decay tunnel
 /det/addFORMOSA true
-/det/formosa/addFORMOSAPos 0 -2500 25996 mm
+/det/formosa/addFORMOSAPos 0 -2500 27033.5 mm
diff --git a/macros/geometry_options/FPF_hall_Reference.mac b/macros/geometry_options/FPF_hall_Reference.mac
index 80119a8..1ed6b94 100644
--- a/macros/geometry_options/FPF_hall_Reference.mac
+++ b/macros/geometry_options/FPF_hall_Reference.mac
@@ -28,18 +28,15 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 42636 mm
+/det/faser/addFASER2Pos 0 0 41611 mm
 /det/faser/magnetGeom SAMURAI
 /det/faser/magnetField 1. tesla
 /det/faser/magnetWinX 3.0 m
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
diff --git a/macros/geometry_options/FPF_hall_Reference_CP.mac b/macros/geometry_options/FPF_hall_Reference_CP.mac
index 73a71ed..e46be9c 100644
--- a/macros/geometry_options/FPF_hall_Reference_CP.mac
+++ b/macros/geometry_options/FPF_hall_Reference_CP.mac
@@ -29,18 +29,15 @@
 # Configuring FASER2
 # assuming 1.2m gap from FASERnu2
 # (which include veto stations)
-# + 10m decay tunnel
 /det/addFASER2 true
-/det/faser/addFASER2Pos 0 0 40661 mm
+/det/faser/addFASER2Pos 0 0 41736 mm
 /det/faser/magnetGeom CrystalPulling
-/det/faser/magnetField 0.6. tesla
+/det/faser/magnetField 0.6 tesla
 /det/faser/magnetInnerR 0.8 m
 /det/faser/magnetOuterR 1.2 m
-/det/faser/magnetLengthZ 1.25 m
+/det/faser/magnetWinZ 1.25 m
 /det/faser/magnetNumber 3
 /det/faser/magnetGap 0.25 m
 /det/faser/trackingNumber 3
-/det/faser/trackingGap 0.1 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
+/det/faser/trackingGap 0.5 m
 /det/faser/trackingScinThick 1.0 cm
diff --git a/src/DetectorConstruction.cc b/src/DetectorConstruction.cc
index 30c50c9..745c86d 100644
--- a/src/DetectorConstruction.cc
+++ b/src/DetectorConstruction.cc
@@ -213,8 +213,9 @@ G4VPhysicalVolume* DetectorConstruction::Construct()
   if (m_addFASER2) {
     FASER2DetectorConstruction *magnetAssembler = new FASER2DetectorConstruction();
     FASER2MagnetLogical = magnetAssembler->GetMagneticVolume(); //need to assign B field
-    TrackingVerScinBarLogical = magnetAssembler->GetVerTrackingScinBar(); //need to assign SD
-    TrackingHorScinBarLogical = magnetAssembler->GetHorTrackingScinBar(); //need to assign SD
+    FASER2TrackingLogical = magnetAssembler->GetTrackingStations();
+    FASER2VetoLogical = magnetAssembler->GetVetoStations();
+    FASER2MuonLogical = magnetAssembler->GetMuonStations();
     G4LogicalVolume* FASER2Assembly = magnetAssembler->GetFASER2Assembly();
 
     // positioning
@@ -360,16 +361,25 @@ void DetectorConstruction::ConstructSDandField() {
   }
 
   if (m_addFASER2) {
-    LArBoxSD* TrkHorScinSD = new LArBoxSD("TrkHorScinSD");
-    TrackingHorScinBarLogical->SetSensitiveDetector(TrkHorScinSD);
-    sdManager->AddNewDetector(TrkHorScinSD);
-    GeometricalParameters::Get()->AddSD2List(SDIdx, "TrkHorScinSD/lar_box");
+    for (auto& station: FASER2TrackingLogical)
+    {
+      LArBoxSD* TrkScinSD = new LArBoxSD("FASER2TrackerSD"+ std::to_string(SDIdx));
+      station->SetSensitiveDetector(TrkScinSD);
+      sdManager->AddNewDetector(TrkScinSD);
+      GeometricalParameters::Get()->AddSD2List(SDIdx, "FASER2TrackerSD"+ std::to_string(SDIdx)+"/lar_box");
+      SDIdx++;
+    }
+
+    LArBoxSD* FASER2VetoSD = new LArBoxSD("FASER2VetoSD");
+    FASER2VetoLogical->SetSensitiveDetector(FASER2VetoSD);
+    sdManager->AddNewDetector(FASER2VetoSD);
+    GeometricalParameters::Get()->AddSD2List(SDIdx, "FASER2VetoSD/lar_box");
     SDIdx++;
 
-    LArBoxSD* TrkVerScinSD = new LArBoxSD("TrkVerScinSD");
-    TrackingVerScinBarLogical->SetSensitiveDetector(TrkVerScinSD);
-    sdManager->AddNewDetector(TrkVerScinSD);
-    GeometricalParameters::Get()->AddSD2List(SDIdx, "TrkVerScinSD/lar_box");
+    LArBoxSD* FASER2MuonDetSD = new LArBoxSD("FASER2MuonSD");
+    FASER2MuonLogical->SetSensitiveDetector(FASER2MuonDetSD);
+    sdManager->AddNewDetector(FASER2MuonDetSD);
+    GeometricalParameters::Get()->AddSD2List(SDIdx, "FASER2MuonSD/lar_box");
     SDIdx++;
 
     // FASER2 magnetic field
diff --git a/src/DetectorConstructionMessenger.cc b/src/DetectorConstructionMessenger.cc
index 8574af9..b5f2c5e 100644
--- a/src/DetectorConstructionMessenger.cc
+++ b/src/DetectorConstructionMessenger.cc
@@ -200,23 +200,21 @@ DetectorConstructionMessenger::DetectorConstructionMessenger(DetectorConstructio
     faserMagnetOuterRCmd = new G4UIcmdWithADoubleAndUnit("/det/faser/magnetOuterR", this);
     faserMagnetOuterRCmd->SetUnitCategory("Length");
     faserMagnetOuterRCmd->SetUnitCandidates("cm m mm");
-    faserMagnetLengthZCmd = new G4UIcmdWithADoubleAndUnit("/det/faser/magnetLengthZ", this);
-    faserMagnetLengthZCmd->SetUnitCategory("Length");
-    faserMagnetLengthZCmd->SetUnitCandidates("cm m mm");
     faserMagnetGapCmd = new G4UIcmdWithADoubleAndUnit("/det/faser/magnetGap", this);
     faserMagnetGapCmd->SetUnitCategory("Length");
     faserMagnetGapCmd->SetUnitCandidates("cm m mm");
     faserMagnetNumberCmd = new G4UIcmdWithAnInteger("/det/faser/magnetNumber", this);
     // Tracking stations
     faserTrackingNumberCmd = new G4UIcmdWithAnInteger("/det/faser/trackingNumber", this);
-    faserTrackingNBarsYCmd = new G4UIcmdWithAnInteger("/det/faser/trackingNBarsY", this);
-    faserTrackingNBarsXCmd = new G4UIcmdWithAnInteger("/det/faser/trackingNBarsX", this);
     faserTrackingScinThickCmd = new G4UIcmdWithADoubleAndUnit("/det/faser/trackingScinThick", this);
     faserTrackingScinThickCmd->SetUnitCategory("Length");
     faserTrackingScinThickCmd->SetUnitCandidates("cm m mm");
     faserTrackingGapCmd = new G4UIcmdWithADoubleAndUnit("/det/faser/trackingGap", this);
     faserTrackingGapCmd->SetUnitCategory("Length");
     faserTrackingGapCmd->SetUnitCandidates("cm m mm");
+    faserFillCaloAndWallCmd = new G4UIcmdWithABool("/det/faser/fillCaloAndWall", this);
+    faserFillCaloAndWallCmd->SetParameterName("Fill FASER2 'calorimeters' and Iron wall with material", true);
+    faserFillCaloAndWallCmd->SetDefaultValue(false);
 }
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -270,14 +268,12 @@ DetectorConstructionMessenger::~DetectorConstructionMessenger() {
   delete faserYokeThickYCmd;
   delete faserMagnetInnerRCmd;
   delete faserMagnetOuterRCmd;
-  delete faserMagnetLengthZCmd;
   delete faserMagnetGapCmd;
   delete faserMagnetNumberCmd;
   delete faserTrackingNumberCmd;
-  delete faserTrackingNBarsYCmd;
-  delete faserTrackingNBarsXCmd;
   delete faserTrackingScinThickCmd;
   delete faserTrackingGapCmd;
+  delete faserFillCaloAndWallCmd;
 
   delete detDir;
   delete flareDir;
@@ -383,8 +379,6 @@ void DetectorConstructionMessenger::SetNewValue(G4UIcommand* command, G4String n
     GeometricalParameters::Get()->SetFASER2MagnetInnerR(faserMagnetInnerRCmd->ConvertToDimensionedDouble(newValues));
   else if (command == faserMagnetOuterRCmd) 
     GeometricalParameters::Get()->SetFASER2MagnetOuterR(faserMagnetOuterRCmd->ConvertToDimensionedDouble(newValues));
-  else if (command == faserMagnetLengthZCmd) 
-    GeometricalParameters::Get()->SetFASER2MagnetLengthZ(faserMagnetLengthZCmd->ConvertToDimensionedDouble(newValues));
   else if (command == faserMagnetGapCmd) 
     GeometricalParameters::Get()->SetFASER2MagnetGap(faserMagnetGapCmd->ConvertToDimensionedDouble(newValues));
   else if (command == faserMagnetNumberCmd) 
@@ -392,14 +386,12 @@ void DetectorConstructionMessenger::SetNewValue(G4UIcommand* command, G4String n
   //faser2 tracking stations
   else if (command == faserTrackingNumberCmd) 
     GeometricalParameters::Get()->SetNTrackingStations(faserTrackingNumberCmd->GetNewIntValue(newValues));
-  else if (command == faserTrackingNBarsYCmd) 
-    GeometricalParameters::Get()->SetNScintillatorBarsY(faserTrackingNBarsYCmd->GetNewIntValue(newValues));
-  else if (command == faserTrackingNBarsXCmd) 
-    GeometricalParameters::Get()->SetNScintillatorBarsX(faserTrackingNBarsXCmd->GetNewIntValue(newValues));
   else if (command == faserTrackingScinThickCmd) 
     GeometricalParameters::Get()->SetScintillatorThickness(faserTrackingScinThickCmd->ConvertToDimensionedDouble(newValues));
   else if (command == faserTrackingGapCmd) 
     GeometricalParameters::Get()->SetTrackingStationGap(faserTrackingGapCmd->ConvertToDimensionedDouble(newValues));
+  else if (command == faserFillCaloAndWallCmd)
+    GeometricalParameters::Get()->SetFillCaloAndWallVolumes(faserFillCaloAndWallCmd->GetNewBoolValue(newValues));
 }
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
diff --git a/src/generators/HepMCGenerator.cc b/src/generators/HepMCGenerator.cc
index b038f5a..560c0ef 100644
--- a/src/generators/HepMCGenerator.cc
+++ b/src/generators/HepMCGenerator.cc
@@ -1,6 +1,7 @@
 #include "generators/GeneratorBase.hh"
 #include "generators/HepMCGenerator.hh"
 #include "generators/HepMCGeneratorMessenger.hh"
+#include "geometry/GeometricalParameters.hh"
 
 #include "HepMC3/ReaderAscii.h"
 #include "HepMC3/ReaderAsciiHepMC2.h"
@@ -12,6 +13,9 @@
 #include "G4TransportationManager.hh"
 #include "G4Navigator.hh"
 #include "G4RunManager.hh"
+#include "G4PhysicalVolumeStore.hh"
+#include "G4Box.hh"
+
 
 HepMCGenerator::HepMCGenerator()
 {
@@ -67,6 +71,17 @@ G4bool HepMCGenerator::CheckVertexInsideWorld(const G4ThreeVector& pos) const
 }
 
 
+static G4VPhysicalVolume* FindPhysicalVolumeByName(const G4String& name) {
+  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
+  for (auto pv : *pvStore) {
+      if (pv->GetName() == name) { 
+          return pv; 
+      }
+  }
+  return nullptr; // Return nullptr if no match is found
+}
+
+
 void HepMCGenerator::GeneratePrimaries(G4Event* anEvent)
 {
 
@@ -88,6 +103,22 @@ void HepMCGenerator::GeneratePrimaries(G4Event* anEvent)
 }
 
 
+G4double HepMCGenerator::GetStartOfDecayVolume()
+{   
+    G4double hallZOffset = GeometricalParameters::Get()->GetHallHeadDistance();
+    G4double FASER2ZOffset = GeometricalParameters::Get()->GetFASER2Position().z();
+    G4double FASER2ZLength = GeometricalParameters::Get()->GetFASER2TotalSizeZ();
+    G4double decayVolumeLength = GeometricalParameters::Get()->GetFASER2DecayVolumeLength();
+    G4double ScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+    G4double VetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+    G4double vetoLength = 2*ScinThickness + VetoShieldThickness;
+
+    G4double decayVolStartZ = hallZOffset  + FASER2ZOffset - FASER2ZLength/2 + vetoLength;
+
+    return decayVolStartZ;
+}
+
+
 void HepMCGenerator::HepMC2G4(const std::shared_ptr<HepMC3::GenEvent> hepmcevt, G4Event* g4event)
 {
   for (const auto& vertex : hepmcevt->vertices()) {
@@ -109,6 +140,18 @@ void HepMCGenerator::HepMC2G4(const std::shared_ptr<HepMC3::GenEvent> hepmcevt,
       continue;
     }
 
+    // If requested; work out where the decay volume starts and use that to offset the vertex so that it falls inside it
+    //! Note: for this to work two assumptions are made
+    //! 1. The vertices within the HepMC file start from (0,0,0). If this is not true then /gen/hepmc/vtxOffset <x y z> MUST be set in the steering macro
+    //! 2. The assumed decay volume length in FORESEE is the same as in this simulation
+    if (fPlaceInDecayVolume)
+    { 
+      G4double decay_vol_start = GetStartOfDecayVolume();
+      // std::cout << "Decay volume starts at " << decay_vol_start << std::endl;
+      xvtx = G4LorentzVector(pos.x(), pos.y(), decay_vol_start+pos.z()+vtx_z_offset, pos.t());
+    }
+
+
     // create G4PrimaryVertex and associated G4PrimaryParticles
     G4PrimaryVertex* g4vtx = new G4PrimaryVertex(xvtx.x(), xvtx.y(), xvtx.z(), xvtx.t());
 
diff --git a/src/generators/HepMCGeneratorMessenger.cc b/src/generators/HepMCGeneratorMessenger.cc
index c013f42..f0285e2 100644
--- a/src/generators/HepMCGeneratorMessenger.cc
+++ b/src/generators/HepMCGeneratorMessenger.cc
@@ -31,6 +31,11 @@ HepMCGeneratorMessenger::HepMCGeneratorMessenger(HepMCGenerator* action)
   fUseHepMC2Cmd->SetGuidance("set generator to hepmc2");
   fUseHepMC2Cmd->SetParameterName("useHepMC2", true);
   fUseHepMC2Cmd->SetDefaultValue(false);
+
+  fHepMCPlaceInDecayVolumeCmd = new G4UIcmdWithABool("/gen/hepmc/placeInDecayVolume", this);
+  fHepMCPlaceInDecayVolumeCmd->SetGuidance("will try and translate vertex into FASER2 decay volume. Note: Assumes that vertices in HepMC start from (0,0,0) - set /hepmc/vtxOffset if not. Also assumes that decay volume lengths match.");
+  fHepMCPlaceInDecayVolumeCmd->AvailableForStates(G4State_PreInit, G4State_Init, G4State_Idle);
+  fHepMCPlaceInDecayVolumeCmd->SetDefaultValue(true);
 }
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -41,6 +46,7 @@ HepMCGeneratorMessenger::~HepMCGeneratorMessenger()
   delete fHepMCVertexOffsetCmd;
   delete fUseHepMC2Cmd;
   delete fHepMCGeneratorDir;
+  delete fHepMCPlaceInDecayVolumeCmd;
 }
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -50,6 +56,7 @@ void HepMCGeneratorMessenger::SetNewValue(G4UIcommand* command, G4String newValu
   if (command == fHepMCInputFileCmd) fHepMCAction->SetHepMCFilename(newValues);
   else if (command == fHepMCVertexOffsetCmd) fHepMCAction->SetHepMCVertexOffset(fHepMCVertexOffsetCmd->GetNew3VectorValue(newValues));
   else if (command == fUseHepMC2Cmd) fHepMCAction->SetUseHepMC2(fUseHepMC2Cmd->GetNewBoolValue(newValues));
+  else if (command == fHepMCPlaceInDecayVolumeCmd) fHepMCAction->SetPlaceInDecayVolume(fHepMCPlaceInDecayVolumeCmd->GetNewBoolValue(newValues));
 }
 
 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
diff --git a/src/geometry/FASER2DetectorConstruction.cc b/src/geometry/FASER2DetectorConstruction.cc
index 0e60adb..c176e95 100644
--- a/src/geometry/FASER2DetectorConstruction.cc
+++ b/src/geometry/FASER2DetectorConstruction.cc
@@ -21,152 +21,182 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
+  fDecayVolumeLength = GeometricalParameters::Get()->GetFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+  G4bool fillCaloAndWallVolumes = GeometricalParameters::Get()->GetFillCaloAndWallVolumes();
 
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double hallZOffset = GeometricalParameters::Get()->GetHallHeadDistance();
+  G4double FASER2ZOffset = GeometricalParameters::Get()->GetFASER2Position().z();
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNTrackingStations*fScinThickness + (fNTrackingStations - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetWindowZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNTrackingStations*fScinThickness + (fNTrackingStations - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  // Set the total size of the FASER2 detector in the GeometricalParameters so that it can be accessed elsewhere
+  GeometricalParameters::Get()->SetMagnetTotalSizeZ(magnetLength);
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(upstreamTrackerLength);
+  GeometricalParameters::Get()->SetMagnetZPosition(hallZOffset + FASER2ZOffset - totLengthZ/2 + vetoLength + fDecayVolumeLength + upstreamTrackerLength + magnetLength/2);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
   
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  //* ------------ Place components in order that they appear ------------ *// 
+  
+  G4double componentPosition = -totLengthZ/2; //* We'll increment this variable as we move through the detector to place things
+  
+  //* Place veto stations
+  componentPosition += fScinThickness/2;
+  G4VSolid* vetoScinBox = new G4Box("vetoScinBox", fVetoLengthX/2, fVetoLengthY/2, fScinThickness/2);
+  fVetoScinLogical = new G4LogicalVolume(vetoScinBox, fMaterials->Material("Polystyrene"), "VetoScinLogical");  
+  new G4PVPlacement(noRot, G4ThreeVector(0,0, componentPosition), fVetoScinLogical, "Veto1ScinPhysical", fFASER2Assembly, false, 0, true);
+
+  componentPosition += fScinThickness/2 + fVetoShieldThickness/2;
+  G4VSolid* vetoShieldBox = new G4Box("vetoShieldBox", fVetoLengthX/2, fVetoLengthY/2, fVetoShieldThickness/2);
+  fVetoShieldLogical = new G4LogicalVolume(vetoShieldBox, fMaterials->Material("Lead"), "VetoShieldLogical");
+  new G4PVPlacement(noRot, G4ThreeVector(0,0, componentPosition), fVetoShieldLogical, "VetoShieldPhysical", fFASER2Assembly, false, 0, true);
+
+  componentPosition += fVetoShieldThickness/2 + fScinThickness/2;
+  new G4PVPlacement(noRot, G4ThreeVector(0,0, componentPosition), fVetoScinLogical, "Veto2ScinPhysical", fFASER2Assembly, false, 0, true);
+
+  //* Place decay volume
+  componentPosition += fScinThickness/2 + fDecayVolumeLength/2; //* We'll increment this variable as we move through the detector to place things
+  G4VSolid* decayVolBox = new G4Box("decayVolBox", GeometricalParameters::Get()->GetFASER2MagnetWindowX()/2, GeometricalParameters::Get()->GetFASER2MagnetWindowY()/2, fDecayVolumeLength/2);
+  fDecayVolumeLogical  = new G4LogicalVolume(decayVolBox, fMaterials->Material("Air"), "FASER2DecayVolLogical");
+  
+  new G4PVPlacement(noRot, G4ThreeVector(0,0, componentPosition), fDecayVolumeLogical, "FASER2DecayVolPhysical", fFASER2Assembly, false, 0, true);
+  
+  //* Place upstream tracking stations
+  componentPosition += fDecayVolumeLength/2 + fScinThickness/2.;
+  for (int i= 0; i<fNTrackingStations; ++i) { 
+
+    std::string name = "F2UpstreamTrackerLayer_" + std::to_string(i+1);
+    auto trkLayerSolid = new G4Box(name+"Box", fTrackingStationX/2, fTrackingStationY/2., fScinThickness/2.);
+    auto trkLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), name+"Logical");
+    auto trkLayerPhysical = new G4PVPlacement(0, G4ThreeVector(0,0,componentPosition), trkLayerLogical, name+"Phys", fFASER2Assembly, false, 0);
+    fTrackingStationsLogical.push_back(trkLayerLogical);
+    
+    if (i != fNTrackingStations-1)
+    {
+      componentPosition += fScinThickness + fTrackingStationGap;
     }
+  }
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  //* Place magnet(s)
+  componentPosition += fScinThickness/2 + fUpstreamTrackingStationGap;
+  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
     
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = 2*fMagnetInnerR + 0.5*m ; //match magnet size + bending plane (for now, FIXME?)
-    fTrackingStationY = 2*fMagnetInnerR; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double stationThickness = 2*fScinThickness;
-
-    // length of a tracking station set (tracking stations + gaps between them)
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap; 
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
+    componentPosition += fMagnetWindowZ/2;
+    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
+    BuildSAMURAIDesign(); //sets logical volumes
     
-    // length of magnet assembly (includes all magnets and tracking stations sets between them + gaps);
-    // it differs from the total length for the two external tracking stations sets
-    G4double magnetsLengthZ = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*(2*fMagnetGap+totThickness);
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(magnetsLengthZ);
-     
-    // total length (all magnets, all tracking stations sets, all gaps)
-    G4double totalLengthZ = 2*(totThickness+fMagnetGap) + magnetsLengthZ;
-    G4double totalLengthY = 2*fMagnetOuterR;
-    G4double totalLengthX = 2*fMagnetOuterR;
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totalLengthZ);    
-
-    BuildCrystalPullingDesign(); //sets logical volumes
-    BuildTrackingStation();
+    // placing magnet + yoke
+    new G4PVPlacement(noRot, G4ThreeVector(0,0,componentPosition), fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
+    new G4PVPlacement(noRot, G4ThreeVector(0,0,componentPosition), fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
+  }
+  else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
     
-    auto containerSolid = new G4Box("FASER2Solid", totalLengthX/2., totalLengthY/2., totalLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
-
-    // center of the assembly is the center of middle magnet window
-    // if fNMagnets is even, it falls in the gap between the two middle magnets
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
-   
-    // each magnet comes with a set of N tracking stations just before it
-    // the gap between each magnet is then given by magnetSpacing
-    G4double magnetSpacing = 2*fMagnetGap + totThickness;
-    GeometricalParameters::Get()->SetFASER2MagnetSpacing(magnetSpacing);    
-
-    for(int i=0; i<fNMagnets; i++){
-      G4double offset = (i-0.5*(fNMagnets-1))*(magnetSpacing+fMagnetLengthZ);
-      G4ThreeVector magPos = magCenter + G4ThreeVector(0.,0.,offset);
-      new G4PVPlacement(noRot, magPos, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, i, false);
-      new G4PVPlacement(noRot, magPos, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, i, false);
-
-      // placing tracking stations (before each magnet)
-      G4double trackingStationOffset = -fMagnetGap-0.5*fMagnetLengthZ;
-      for (int j= 0; j<fNTrackingStations; ++j) { 
-        G4ThreeVector T(0., 0., trackingStationOffset-j*fTrackingStationGap-0.5*(i+1)*stationThickness);
-        G4ThreeVector Tpos = magPos + T; 
-        fTrackingStation->MakeImprint(fFASER2Assembly, Tpos, noRot, i, false); 
-      }
-    }
+    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
+    BuildCrystalPullingDesign();
     
-    // placing last set of tracking stations (after last magnet)
-    G4ThreeVector offset(0, 0, magnetsLengthZ/2.+ fMagnetGap + totThickness/2.);
-    G4ThreeVector postStationsCenter = magCenter + offset;
-
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, fNMagnets, false);
+    componentPosition += fMagnetWindowZ/2;
+    for(int i=0; i<fNMagnets; i++)
+    {  
+        G4ThreeVector magPos = G4ThreeVector(0.,0.,componentPosition);
+        new G4PVPlacement(noRot, magPos, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, i, false);
+        new G4PVPlacement(noRot, magPos, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, i, false);
+        if (i != fNMagnets-1){
+          componentPosition += fMagnetGap + fMagnetWindowZ;
+        }
     }
-   
-  } else {
+  } 
+  else {
     G4cout << "ERROR: unknown FASER2 spectrometer magnet option!" << G4endl;  
   }
 
-  // visibility
-  G4VisAttributes* nullVis = new G4VisAttributes(G4Colour(167./255, 168./255, 189./255));
-  nullVis->SetVisibility(false);
-  fMagnetWindow->SetVisAttributes(nullVis);
+  //* Place upstream tracking stations
+  componentPosition += fMagnetWindowZ/2 + fDownstreamTrackingStationGap;
+  for (int i= 0; i<fNTrackingStations; ++i) { 
 
-  G4VisAttributes* magnetVis = new G4VisAttributes(G4Colour(234./255, 173./255, 26./255, 0.8));
-  magnetVis->SetVisibility(true);
-  fMagnetYoke->SetVisAttributes(magnetVis);
+    std::string name = "F2DownstreamTrackerLayer_" + std::to_string(i+1);
+    auto trkLayerSolid = new G4Box(name+"Box", fTrackingStationX/2, fTrackingStationY/2., fScinThickness/2.);
+    auto trkLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), name+"Logical");
+    auto trkLayerPhysical = new G4PVPlacement(0, G4ThreeVector(0,0,componentPosition), trkLayerLogical, name+"Phys", fFASER2Assembly, false, 0);
+    fTrackingStationsLogical.push_back(trkLayerLogical);
+
+    componentPosition += fScinThickness + fTrackingStationGap;
+  }
   
-  G4VisAttributes* stationVis = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
-  stationVis->SetVisibility(true);
-  fHorTrackingScinBar->SetVisAttributes(stationVis);  
-  fVerTrackingScinBar->SetVisAttributes(stationVis);  
+  //* Place 'calorimeters' and iron wall
+  //TODO: Calorimeter volumes are just dummy placeholder boxes - detailed simulation studies required!
+  auto ECalBox = new G4Box("ECalBox", fMagnetWindowX/2.+fMagnetYokeThicknessX, fMagnetWindowY/2.+fMagnetYokeThicknessY, fEMCaloThickness/2.);
+  auto HCalBox = new G4Box("HCalBox", fMagnetWindowX/2.+fMagnetYokeThicknessX, fMagnetWindowY/2.+fMagnetYokeThicknessY, fHadCaloThickness/2.);
+  auto IronWallBox = new G4Box("HCalBox", fMagnetWindowX/2.+fMagnetYokeThicknessX, fMagnetWindowY/2.+fMagnetYokeThicknessY, fIronWallThickness/2.);
+
+  fEMCalLogical = new G4LogicalVolume(ECalBox, (fillCaloAndWallVolumes) ? fMaterials->Material("Copper"): fMaterials->Material("Air"), "FASER2ECAlLogical");
+  fHadCalLogical = new G4LogicalVolume(HCalBox, (fillCaloAndWallVolumes) ? fMaterials->Material("Iron"): fMaterials->Material("Air"), "FASER2HCAlLogical");
+  fIronWallLogical = new G4LogicalVolume(IronWallBox, (fillCaloAndWallVolumes) ? fMaterials->Material("Iron"): fMaterials->Material("Air"), "FASER2IronWallLogical");
+  
+  componentPosition += fEMCaloThickness/2;
+  new G4PVPlacement(noRot, G4ThreeVector(0,0,componentPosition), fEMCalLogical, "FASER2ECalPhysical", fFASER2Assembly, false, 0, false);
+  componentPosition += fEMCaloThickness/2 + fHadCaloThickness/2;
+  new G4PVPlacement(noRot, G4ThreeVector(0,0,componentPosition), fHadCalLogical, "FASER2HCalPhysical", fFASER2Assembly, false, 0, false);
+  componentPosition += fHadCaloThickness/2 + fIronWallThickness/2;
+  new G4PVPlacement(noRot, G4ThreeVector(0,0,componentPosition), fIronWallLogical, "FASERIronWallPhysical", fFASER2Assembly, false, 0, false);
+  componentPosition += fIronWallThickness/2;
+
+  //* Add muon detector
+  componentPosition += fTrackingStationGap + fScinThickness/2;
+  auto muonDetSolid = new G4Box("muonTrkLayerBox", fMagnetWindowX/2.+fMagnetYokeThicknessX, fMagnetWindowY/2.+fMagnetYokeThicknessY, fScinThickness/2.);
+  fMuonDetLogical = new G4LogicalVolume(muonDetSolid, fMaterials->Material("Polystyrene"), "muonTrkLayerLogical");
+  auto muonDetPhysical = new G4PVPlacement(0, G4ThreeVector(0,0,componentPosition), fMuonDetLogical, "muonTrkLayerPhys", fFASER2Assembly, false, 0);
+
+  //* Set visualisation attributes for all components
+  SetVisualisation();
+
 }
 
 FASER2DetectorConstruction::~FASER2DetectorConstruction()
@@ -192,41 +222,82 @@ void FASER2DetectorConstruction::BuildSAMURAIDesign()
 
 void FASER2DetectorConstruction::BuildCrystalPullingDesign()
 {
-  auto magnetWindowSolid = new G4Tubs("MagnetWindow",0.,fMagnetInnerR,fMagnetLengthZ/2.,0.,CLHEP::twopi);
-  auto magnetYokeSolid = new G4Tubs("MagnetYoke",fMagnetInnerR,fMagnetOuterR,fMagnetLengthZ/2.,0.,CLHEP::twopi);
+  auto magnetWindowSolid = new G4Tubs("MagnetWindow",0.,fMagnetInnerR,fMagnetWindowZ/2.,0.,CLHEP::twopi);
+  auto magnetYokeSolid = new G4Tubs("MagnetYoke",fMagnetInnerR,fMagnetOuterR,fMagnetWindowZ/2.,0.,CLHEP::twopi);
 
   fMagnetYoke = new G4LogicalVolume(magnetYokeSolid, fMaterials->Material("Iron"), "FASER2MagnetYokeLogical");
   fMagnetWindow = new G4LogicalVolume(magnetWindowSolid, fMaterials->Material("Air"), "FASER2MagnetWindowLogical");
 }
 
-void FASER2DetectorConstruction::BuildTrackingStation()
+
+void FASER2DetectorConstruction::SetVisualisation()
 {
-  // Each tracking station is made of 2 layers
-  // 1st layer: fNScinBarsY horizontal modules
-  // 2nd layer: fNScinBarsX vertical modules
-  G4double horizontalScinSize = fTrackingStationY / fNScinBarsY; // y size of horizontal scin
-  G4double verticalScinSize = fTrackingStationX / fNScinBarsX; // x size of vertical scin
-
-  // layer volume: same size, but first has horizontal bars, second one has vertical bars
-  auto trkLayerSolid = new G4Box("trkLayerBox", fTrackingStationX/2, fTrackingStationY/2., fScinThickness/2.);
-  auto trkHorLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkHorLayerLogical"); 
-  auto trkVerLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkVerLayerLogical"); 
-  
-  // first layer is made of 2 horizontal pieces
-  auto trkHorScinSolid = new G4Box("trkHorScinSolid", fTrackingStationX/2., horizontalScinSize/2., fScinThickness/2.);
-  fHorTrackingScinBar = new G4LogicalVolume(trkHorScinSolid, fMaterials->Material("Polystyrene"), "trkHorScinLogical");
-  new G4PVReplica("trkHorScinPhysical", fHorTrackingScinBar,
-                  trkHorLayerLogical, kYAxis, fNScinBarsY, horizontalScinSize);
-  // secondo layer is made of 7 vertical pieces
-  auto trkVerScinSolid = new G4Box("trkHorScinSolid", verticalScinSize/2., fTrackingStationY/2., fScinThickness/2.);
-  fVerTrackingScinBar = new G4LogicalVolume(trkVerScinSolid, fMaterials->Material("Polystyrene"), "trkVerScinLogical");
-  new G4PVReplica("trkVerScinPhysical", fVerTrackingScinBar,
-                  trkVerLayerLogical, kXAxis, fNScinBarsX, verticalScinSize);
+  //? Moved all the detector visualisation settings to this function - perhaps in future we could set these with a messenger class or config file?
+  //* Detector bounding box
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
+
+  //* Veto station
+  G4VisAttributes* vetoScin_logVisAtt = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
+  vetoScin_logVisAtt->SetForceWireframe(true);
+  vetoScin_logVisAtt->SetForceSolid(false);
+  vetoScin_logVisAtt->SetVisibility(true);
+  fVetoScinLogical->SetVisAttributes(vetoScin_logVisAtt);
+
+  G4VisAttributes* vetoShield_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  vetoShield_logVisAtt->SetForceWireframe(true);
+  vetoShield_logVisAtt->SetForceSolid(true);
+  vetoShield_logVisAtt->SetVisibility(true);
+  fVetoShieldLogical->SetVisAttributes(vetoShield_logVisAtt);
+
+  //* Decay volume
+  G4VisAttributes* DV_logVisAtt = new G4VisAttributes(G4Colour(0.8,0.8,0.8,0.3));
+  DV_logVisAtt->SetForceWireframe(true);
+  DV_logVisAtt->SetForceSolid(true);
+  fDecayVolumeLogical->SetVisAttributes(DV_logVisAtt);
+
+  //* Tracking stations
+  for (auto trkLayerLogical : fTrackingStationsLogical)
+  {
+    G4VisAttributes* stationVis = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
+    stationVis->SetVisibility(true);
+    trkLayerLogical->SetVisAttributes(stationVis);
+  }
+
+  //* Magnets
+  G4VisAttributes* nullVis = new G4VisAttributes(G4Colour(167./255, 168./255, 189./255));
+  nullVis->SetVisibility(false);
+  fMagnetWindow->SetVisAttributes(nullVis);
+
+  G4VisAttributes* magnetVis = new G4VisAttributes(G4Colour(234./255, 173./255, 26./255, 0.8));
+  magnetVis->SetVisibility(true);
+  magnetVis->SetForceSolid(true);
+  fMagnetYoke->SetVisAttributes(magnetVis);
+
+  //* 'Calorimeters' & iron wall
+  G4VisAttributes* ECalVis = new G4VisAttributes(G4Colour::Red());
+  ECalVis->SetVisibility(true);
+  // ECalVis->SetForceSolid(true);
+  ECalVis->SetForceWireframe(true);
+  fEMCalLogical->SetVisAttributes(ECalVis);
   
-  fTrackingStation = new G4AssemblyVolume(); //one assembly has 2 layers
-  G4RotationMatrix rot(0, 0, 0);
-  G4ThreeVector pos(0, 0, -fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkHorLayerLogical,pos,&rot);
-  pos.setZ(fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkVerLayerLogical,pos,&rot);
-}
+  G4VisAttributes* HCalVis = new G4VisAttributes(G4Colour::Brown());
+  HCalVis->SetVisibility(true);
+  // HCalVis->SetForceSolid(true);
+  HCalVis->SetForceWireframe(true);
+  fHadCalLogical->SetVisAttributes(HCalVis);
+
+  G4VisAttributes* IronWallVis = new G4VisAttributes(G4Colour::Grey());
+  IronWallVis->SetVisibility(true);
+  // IronWallVis->SetForceSolid(true);
+  IronWallVis->SetForceWireframe(true);
+  fIronWallLogical->SetVisAttributes(IronWallVis);
+
+  //* Muon detector
+  G4VisAttributes* stationVis = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
+  stationVis->SetVisibility(true);
+  fMuonDetLogical->SetVisAttributes(stationVis);
+} 
\ No newline at end of file
diff --git a/src/geometry/GeometricalParameters.cc b/src/geometry/GeometricalParameters.cc
index c159b2c..61013c9 100644
--- a/src/geometry/GeometricalParameters.cc
+++ b/src/geometry/GeometricalParameters.cc
@@ -53,27 +53,37 @@ GeometricalParameters::GeometricalParameters()
   fMagnetTotalSizeZ = 4*m; //updates during construction
   fTrackingStationTotalSizeZ = 2.62*m; //updates during construction
   fMagnetZPos = 40*m; //updates during construction
-  fFASER2TotalSizeZ = 10.24*m;
+  fFASER2TotalSizeZ = 25*m; //updates during construction
+  fFASER2DecayVolumeLength = 10*m;
   // SAMURAI design
   fFASER2MagnetWindowX = 3.0*m;
   fFASER2MagnetWindowY = 1.0*m;
   fFASER2MagnetWindowZ = 4.0*m;
   fFASER2MagnetYokeThickX = 1.5*m;
-  fFASER2MagnetYokeThickY = 2.0*m;
+  fFASER2MagnetYokeThickY = 0.5*m;
   // CrystalPulling design
-  fFASER2MagnetLengthZ = 1.25*m;
   fFASER2MagnetInnerR = 0.8*m;
   fFASER2MagnetOuterR = 1.2*m;
   fNFASER2Magnets = 3;
   fFASER2MagnetGap = 0.25*m;
   fFASER2MagnetSpacing = 0.76*m; //updates during construction
   // Tracking stations
-  fNTrackingStations = 6;
-  fNScintillatorBarsY = 3;
-  fNScintillatorBarsX = 7;
+  fNTrackingStations = 3;
   fScintillatorThickness = 1*cm;
   fTrackingStationGap = 0.5*m;
   fFASER2Pos = G4ThreeVector(0., 0., 42636.*mm);
+  fFASER2TrackerX = 3*m;
+  fFASER2TrackerY = 1*m;  
+  fFASER2UpstreamTrackingStationGap = 0.5*m;
+  fFASER2DownstreamTrackingStationGap = 4*m;
+
+  fFASER2FillCaloAndWall = true; //! Simulating the additional interactions in Copper/Iron is time consuming and often not needed 
+  fFASER2EMCaloThickness = 1*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2HadCaloThickness = 2*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2IronWallThickness = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthX = 4*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthY = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoShieldThickness = 10*cm; //! These values are placeholders and require dedicated optmisation studies!
 
   // FASERnu2 emulsion detector
   fFASERnu2TotalSizeZ = 8.5*m; //updates during construction
diff --git a/src/reco/CircleFit.cc b/src/reco/CircleFit.cc
index 7337650..e26fb5c 100644
--- a/src/reco/CircleFit.cc
+++ b/src/reco/CircleFit.cc
@@ -200,7 +200,7 @@ namespace circularfitter {
     }else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
       
       double zcenter = GeometricalParameters::Get()->GetMagnetZPosition();
-      double size = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+      double size = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
       double spacing = GeometricalParameters::Get()->GetFASER2MagnetSpacing();
       nmag = GeometricalParameters::Get()->GetNFASER2Magnets();
       for(int i=0; i<nmag; i++){


Reviews:
user: login: mvicenzi
body: Thank you so much for all this work!! I left a few comments, some are just very pendantic but I do have some general things to point out:

1. In `HepMCGenerator`: my suggestion is to look for a solution that uses `GeometricalParameters` instead of looping and working through all physical volumes to find the start of the decay volume.
2. In `FASER2DetectorConstruction` & friends: I'm skeptical about using a `std::vector` of logical volumes for the tracking stations.   I'm not sure it would work since they're all copies of the same volume... and generally it is better to improve the hit structure to save the copy number of the physical volume it comes from, rather than having multiple identical logical/SD volumes for each replica.
3. The change in the size of the FASER2 assembly will require a change in the position in the hall for all the default configurations. I can help figuring it out.

It would also be good to review what variables are being used and purge all the others. I guess this is probably something that can be done while setting the messenger commands.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T17:22:09Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T10:32:32Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T15:16:32Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T15:49:19Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T16:02:28Z
user: login: mvicenzi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T16:55:57Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T17:01:02Z
user: login: mvicenzi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T17:42:50Z
user: login: mvicenzi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T17:59:40Z
user: login: mvicenzi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-26T17:59:53Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T10:39:11Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T10:44:08Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T11:08:49Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T11:15:12Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T13:42:51Z
user: login: OlivierSalin
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-27T18:07:32Z
user: login: mvicenzi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T19:53:56Z
user: login: mvicenzi
body: I believe all my points were addressed, so I approve.

@benw22022 I really appreciate you putting up with my requests.
Feel free to merge it once you have the rest of the details in!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-27T19:58:05Z
user: login: benw22022
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-28T10:10:48Z

Review comments:
diff_hunk: @@ -29,6 +29,7 @@ class HepMCGeneratorMessenger: public G4UImessenger
     G4UIcmdWithAString* fHepMCInputFileCmd;
     G4UIcmdWith3VectorAndUnit* fHepMCVertexOffsetCmd;
     G4UIcmdWithABool* fUseHepMC2Cmd;
+    G4UIcmdWithABool* fHepMCPlaceInDecayVolume;
path: include/generators/HepMCGeneratorMessenger.hh
user: login: mvicenzi
body: ```suggestion
    G4UIcmdWithABool* fHepMCPlaceInDecayVolumeCmd;
```
While revamping the generators, my convention was that any `G4UICommand` gets `Cmd` in the name. It would be good if we could maintein it moving forward (.. and expand it to the geometry messenger)
created_at: 2025-03-25T15:20:32Z
author_association: COLLABORATOR
diff_hunk: @@ -31,6 +31,10 @@ HepMCGeneratorMessenger::HepMCGeneratorMessenger(HepMCGenerator* action)
   fUseHepMC2Cmd->SetGuidance("set generator to hepmc2");
   fUseHepMC2Cmd->SetParameterName("useHepMC2", true);
   fUseHepMC2Cmd->SetDefaultValue(false);
+
+  fHepMCPlaceInDecayVolume = new G4UIcmdWithABool("/gen/hepmc/placeInDecayVolume", this);
+  fHepMCPlaceInDecayVolume->SetGuidance("will try and translate vertex into FASER2 decay volume. Note: Assumes that vertices in HepMC start from (0,0,0) - set /hepmc/vtxOffset if not. Also assumes that decay volume lengths match.");
+  fHepMCPlaceInDecayVolume->AvailableForStates(G4State_PreInit, G4State_Init, G4State_Idle);
path: src/generators/HepMCGeneratorMessenger.cc
user: login: mvicenzi
body: ```suggestion
  fHepMCPlaceInDecayVolumeCmd = new G4UIcmdWithABool("/gen/hepmc/placeInDecayVolume", this);
  fHepMCPlaceInDecayVolumeCmd->SetGuidance("will try and translate vertex into FASER2 decay volume. Note: Assumes that vertices in HepMC start from (0,0,0) - set /hepmc/vtxOffset if not. Also assumes that decay volume lengths match.");
  fHepMCPlaceInDecayVolumeCmd->AvailableForStates(G4State_PreInit, G4State_Init, G4State_Idle);
  fHepMCPlaceInDecayVolumeCmd->SetDefaultValue(true);
```
Same comment here for `Cmd`. Also it would be good to add a default value (I assume to best is `true`). It would also be good to add a default value in the constructor of `HepMCGenerator.cc` as well. However, technically setting it in one place should be enough. 
created_at: 2025-03-25T15:26:34Z
author_association: COLLABORATOR
diff_hunk: @@ -41,6 +45,7 @@ HepMCGeneratorMessenger::~HepMCGeneratorMessenger()
   delete fHepMCVertexOffsetCmd;
   delete fUseHepMC2Cmd;
   delete fHepMCGeneratorDir;
+  delete fHepMCPlaceInDecayVolume;
path: src/generators/HepMCGeneratorMessenger.cc
user: login: mvicenzi
body: ```suggestion
  delete fHepMCPlaceInDecayVolumeCmd;
```

created_at: 2025-03-25T15:26:55Z
author_association: COLLABORATOR
diff_hunk: @@ -50,6 +55,7 @@ void HepMCGeneratorMessenger::SetNewValue(G4UIcommand* command, G4String newValu
   if (command == fHepMCInputFileCmd) fHepMCAction->SetHepMCFilename(newValues);
   else if (command == fHepMCVertexOffsetCmd) fHepMCAction->SetHepMCVertexOffset(fHepMCVertexOffsetCmd->GetNew3VectorValue(newValues));
   else if (command == fUseHepMC2Cmd) fHepMCAction->SetUseHepMC2(fUseHepMC2Cmd->GetNewBoolValue(newValues));
+  else if (command == fHepMCPlaceInDecayVolume) fHepMCAction->SetPlaceInDecayVolume(fHepMCPlaceInDecayVolume->GetNewBoolValue(newValues));
path: src/generators/HepMCGeneratorMessenger.cc
user: login: mvicenzi
body: ```suggestion
  else if (command == fHepMCPlaceInDecayVolumeCmd) fHepMCAction->SetPlaceInDecayVolume(fHepMCPlaceInDecayVolumeCmd->GetNewBoolValue(newValues));
```
created_at: 2025-03-25T15:27:14Z
author_association: COLLABORATOR
diff_hunk: @@ -88,6 +102,39 @@ void HepMCGenerator::GeneratePrimaries(G4Event* anEvent)
 }
 
 
+static G4double GetStartOfDecayVolume()
+{
+    G4VPhysicalVolume* DVphysicalVolume = FindPhysicalVolumeByName("FASER2DecayVolPhysical");
path: src/generators/HepMCGenerator.cc
user: login: mvicenzi
body: One general comment: if you need simple geometrical information, I would say the best way is using our `singleton` insteance of `GeometricalParameters`: this can be summoned everywhere simply by including its header and doing:
```
#include "geometry/GeometricalParameters.hh"

G4double myBlaBla = GeometricalParameters::Get()->GetBlaBla();
```
For instance, I assume the half-length of the decay volume can be readily extracted this way without having to work with the physical volume. Positions are a bit trickier, but the absolute FASER2 center position should be there, and the one could work relative to that. You can also add more variables to `GeometricalParameters` as you build and place the geometry if you need more stuff.

The advantage of moving to that implementation would be that:

1. You don't need to loop over all physical volumes (migth become slow?)
2. You don't need to depend explicitly on the volume name, just on the variable name(s) in `GeometricalParameters`.. but I would say it would make it easier to maintain since `GeometricalParameters` is filled alongside the geometry. 

created_at: 2025-03-25T15:44:55Z
author_association: COLLABORATOR
diff_hunk: @@ -88,6 +102,39 @@ void HepMCGenerator::GeneratePrimaries(G4Event* anEvent)
 }
 
 
+static G4double GetStartOfDecayVolume()
+{
+    G4VPhysicalVolume* DVphysicalVolume = FindPhysicalVolumeByName("FASER2DecayVolPhysical");
+    if (!DVphysicalVolume)
+    {
+        std::cout << "ERROR: Tried to retrive the FASER2 decay volume but it does not exist!" << std::endl;
+        return 0;
path: src/generators/HepMCGenerator.cc
user: login: mvicenzi
body: Use `G4cout` or `G4cerr`. You might also want to set up a `G4Exception` if you want it to complain really loudly.
created_at: 2025-03-25T15:46:23Z
author_association: COLLABORATOR
diff_hunk: @@ -109,6 +156,18 @@ void HepMCGenerator::HepMC2G4(const std::shared_ptr<HepMC3::GenEvent> hepmcevt,
       continue;
     }
 
+    // If requested; work out where the decay volume starts and use that to offset the vertex so that it falls inside it
+    //! Note: for this to work two assumptions are made
+    //! 1. The vertices within the HepMC file start from (0,0,0). If this is not true then /hepmc/vtxOffset <x y z> MUST be set in the steering macro
path: src/generators/HepMCGenerator.cc
user: login: mvicenzi
body: ```suggestion
    //! 1. The vertices within the HepMC file start from (0,0,0). If this is not true then /gen/hepmc/vtxOffset <x y z> MUST be set in the steering macro
```
created_at: 2025-03-25T15:47:21Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Totally agree with your comment... please don't hesitate and purge the duplicate!
The only place where this might be used outside of here is `src/reco/CircleFit.cc` but that class will be deprecated anyway by your other changes, so I wouldn't worry.
created_at: 2025-03-25T16:06:27Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Is this needed? Don't you have separate numbers for upstream/downstream now?
created_at: 2025-03-25T16:07:16Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Can you use `magnetLength` to fill `GeometricalParameters::Get()->SetMagnetTotalSizeZ`?
We used to fill it after construction (default value in `GeometricalParameters` was a guess), so that if need to recall it somewhere else we already have the full size. Might actually no longer be needed, but possibly it is still referenced by `src/reco/CircleFit.cc`.
created_at: 2025-03-25T16:16:50Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
+
+  //* ------------ Place components in order that they appear ------------ *// 
+  
+  G4double componentPosition = -totLengthZ/2; //* We'll increment this variable as we move through the detector to place things
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: This is similar to before, so the "center" of the `fFASERAssembly` box is the `(0,0,0)` local reference, right?
If so, since now the full assembly is longer, the offset that comes from the `.mac` file(s) used in `DetectorConstruction` to place FASER2 in the hall needs to be changed in all default configurations.

To make it more clear: in the past `(0,0,0)` was the center of the magnet window (or the center of the central magnet 😅 ), so (for instance) we were adding the `10 m` of the decay volume in the placement offset in the configuration file. This now needs to be changed.
created_at: 2025-03-25T16:29:43Z
author_association: COLLABORATOR
diff_hunk: @@ -60,6 +80,15 @@ class FASER2DetectorConstruction {
     G4int fNScinBarsY;
     G4int fNScinBarsX;
path: include/geometry/FASER2DetectorConstruction.hh
user: login: mvicenzi
body: If no longer needed, please remove all these variables!
```suggestion

```
created_at: 2025-03-25T16:37:53Z
author_association: COLLABORATOR
diff_hunk: @@ -71,8 +72,9 @@ class DetectorConstruction : public G4VUserDetectorConstruction {
 
     // FASER2
     G4LogicalVolume* FASER2MagnetLogical;
-    G4LogicalVolume* TrackingVerScinBarLogical;
-    G4LogicalVolume* TrackingHorScinBarLogical; 
+    std::vector<G4LogicalVolume*> FASER2TrackingLogical;
path: include/DetectorConstruction.hh
user: login: mvicenzi
body: I don't like the idea of a vector of identical logical volumes... Why is it needed? 
These are all identical (same solid, same material)?
created_at: 2025-03-25T16:39:04Z
author_association: COLLABORATOR
diff_hunk: @@ -101,8 +101,7 @@ class DetectorConstructionMessenger: public G4UImessenger {
     G4UIcmdWithAnInteger* faserMagnetNumberCmd;
     // Tracking stations
     G4UIcmdWithAnInteger* faserTrackingNumberCmd;
path: include/DetectorConstructionMessenger.hh
user: login: mvicenzi
body: ```suggestion
    
```
Remove as well. I believe you are using two different parameters for upstream/downstream now?
created_at: 2025-03-25T16:40:15Z
author_association: COLLABORATOR
diff_hunk: @@ -34,9 +37,26 @@ class FASER2DetectorConstruction {
     DetectorConstructionMaterial* fMaterials;
     G4LogicalVolume* fMagnetYoke;
     G4LogicalVolume* fMagnetWindow;
+    G4LogicalVolume* fTrackingLogical;
+    G4LogicalVolume* fVetoScinLogical;
+    G4LogicalVolume* fVetoShieldLogical;
+    G4LogicalVolume* fDecayVolumeLogical;
+    std::vector<G4LogicalVolume*> fTrackingStationsLogical;
path: include/geometry/FASER2DetectorConstruction.hh
user: login: mvicenzi
body: As I mentioned in other comment(s) `fTrackingLogical` shoud be enough. 
The `fTrackingStationsLogical` ends up containing identical logical volumes?

```suggestion
    G4LogicalVolume* fTrackingLogical;
    G4LogicalVolume* fVetoScinLogical;
    G4LogicalVolume* fVetoShieldLogical;
    G4LogicalVolume* fDecayVolumeLogical;
```
created_at: 2025-03-25T16:43:11Z
author_association: COLLABORATOR
diff_hunk: @@ -261,11 +285,24 @@ class GeometricalParameters  {
     G4double fFASER2MagnetSpacing;
     // Tracking stations
     G4int fNTrackingStations;
+    G4double fFASER2TrackerX;
+    G4double fFASER2TrackerY;
     G4int fNScintillatorBarsY;
     G4int fNScintillatorBarsX;
path: include/geometry/GeometricalParameters.hh
user: login: mvicenzi
body: ```suggestion
```
No longer used? Might be good to go over all variables again to clean-up what's not used...
created_at: 2025-03-25T16:44:22Z
author_association: COLLABORATOR
diff_hunk: @@ -360,16 +361,25 @@ void DetectorConstruction::ConstructSDandField() {
   }
 
   if (m_addFASER2) {
-    LArBoxSD* TrkHorScinSD = new LArBoxSD("TrkHorScinSD");
-    TrackingHorScinBarLogical->SetSensitiveDetector(TrkHorScinSD);
-    sdManager->AddNewDetector(TrkHorScinSD);
-    GeometricalParameters::Get()->AddSD2List(SDIdx, "TrkHorScinSD/lar_box");
+    for (auto& station: FASER2TrackingLogical)
+    {
+      LArBoxSD* TrkScinSD = new LArBoxSD("FASER2TrackerSD"+ std::to_string(SDIdx));
+      station->SetSensitiveDetector(TrkScinSD);
+      sdManager->AddNewDetector(TrkScinSD);
+      GeometricalParameters::Get()->AddSD2List(SDIdx, "FASER2TrackerSD"+ std::to_string(SDIdx)+"/lar_box");
+      SDIdx++;
+    }
path: src/DetectorConstruction.cc
user: login: mvicenzi
body: I'm not sure what is gained by doing this... these are all indentical logical volumes? I mean, it's the same volume placed multiple times in the vector?

If we need a way to select hits coming from different physical tracking stations in order to avoid all of them ending up inside the same collection, its better to save the volume copy number in the `lar_box` hit (or maybe creating a different "hit" altogether). Currently I'm not sure this implementation would even work?
created_at: 2025-03-25T16:50:02Z
author_association: COLLABORATOR
diff_hunk: @@ -201,32 +288,19 @@ void FASER2DetectorConstruction::BuildCrystalPullingDesign()
 
 void FASER2DetectorConstruction::BuildTrackingStation()
 {
-  // Each tracking station is made of 2 layers
-  // 1st layer: fNScinBarsY horizontal modules
-  // 2nd layer: fNScinBarsX vertical modules
-  G4double horizontalScinSize = fTrackingStationY / fNScinBarsY; // y size of horizontal scin
-  G4double verticalScinSize = fTrackingStationX / fNScinBarsX; // x size of vertical scin
-
   // layer volume: same size, but first has horizontal bars, second one has vertical bars
   auto trkLayerSolid = new G4Box("trkLayerBox", fTrackingStationX/2, fTrackingStationY/2., fScinThickness/2.);
-  auto trkHorLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkHorLayerLogical"); 
-  auto trkVerLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkVerLayerLogical"); 
-  
-  // first layer is made of 2 horizontal pieces
-  auto trkHorScinSolid = new G4Box("trkHorScinSolid", fTrackingStationX/2., horizontalScinSize/2., fScinThickness/2.);
-  fHorTrackingScinBar = new G4LogicalVolume(trkHorScinSolid, fMaterials->Material("Polystyrene"), "trkHorScinLogical");
-  new G4PVReplica("trkHorScinPhysical", fHorTrackingScinBar,
-                  trkHorLayerLogical, kYAxis, fNScinBarsY, horizontalScinSize);
-  // secondo layer is made of 7 vertical pieces
-  auto trkVerScinSolid = new G4Box("trkHorScinSolid", verticalScinSize/2., fTrackingStationY/2., fScinThickness/2.);
-  fVerTrackingScinBar = new G4LogicalVolume(trkVerScinSolid, fMaterials->Material("Polystyrene"), "trkVerScinLogical");
-  new G4PVReplica("trkVerScinPhysical", fVerTrackingScinBar,
-                  trkVerLayerLogical, kXAxis, fNScinBarsX, verticalScinSize);
-  
-  fTrackingStation = new G4AssemblyVolume(); //one assembly has 2 layers
+  auto trkLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkLayerLogical");
+  
+  G4VisAttributes* stationVis = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
+  stationVis->SetVisibility(true);
+  trkLayerLogical->SetVisAttributes(stationVis);
+
+  fTrackingStation = new G4AssemblyVolume();
   G4RotationMatrix rot(0, 0, 0);
   G4ThreeVector pos(0, 0, -fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkHorLayerLogical,pos,&rot);
   pos.setZ(fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkVerLayerLogical,pos,&rot);
+  auto trkLayerPhysical = new G4PVPlacement(0, pos, trkLayerLogical, "TrkPhys", fFASER2Assembly, false, 0);
+  fTrackingStation->AddPlacedVolume(trkLayerLogical,pos,&rot);
+  fTrackingStationsLogical.push_back(trkLayerLogical);
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Repeating here what I wrote elsewhere: these are all copies of the same logical volume? 
If it's a way to separate hits in different SD collections, I'm not sure it would work?
I'd say it's better to keep the same unique logical volume as SD, then change the hit structure to contain an ID of the physical volume (copy number or similar). This can be done in `LArBoxSD.cc` or creating a new hit type specific for FASER2. 
created_at: 2025-03-25T16:57:08Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: What about moving all the visualization things at the end? Or in an external function that you call at the end? This is just to clean-up the code a bit. It seems that all of these logical volumes are class members, so it should be easy.
created_at: 2025-03-25T17:00:15Z
author_association: COLLABORATOR
diff_hunk: @@ -68,12 +69,22 @@ GeometricalParameters::GeometricalParameters()
   fFASER2MagnetGap = 0.25*m;
   fFASER2MagnetSpacing = 0.76*m; //updates during construction
   // Tracking stations
-  fNTrackingStations = 6;
-  fNScintillatorBarsY = 3;
-  fNScintillatorBarsX = 7;
+  fNTrackingStations = 3;
path: src/geometry/GeometricalParameters.cc
user: login: mvicenzi
body: No used anymore?
```suggestion

```
created_at: 2025-03-25T17:01:32Z
author_association: COLLABORATOR
diff_hunk: @@ -53,13 +53,14 @@ GeometricalParameters::GeometricalParameters()
   fMagnetTotalSizeZ = 4*m; //updates during construction
   fTrackingStationTotalSizeZ = 2.62*m; //updates during construction
   fMagnetZPos = 40*m; //updates during construction
path: src/geometry/GeometricalParameters.cc
user: login: mvicenzi
body: Are these still being filled during construction?
created_at: 2025-03-25T17:02:29Z
author_association: COLLABORATOR
diff_hunk: @@ -53,13 +53,14 @@ GeometricalParameters::GeometricalParameters()
   fMagnetTotalSizeZ = 4*m; //updates during construction
   fTrackingStationTotalSizeZ = 2.62*m; //updates during construction
   fMagnetZPos = 40*m; //updates during construction
-  fFASER2TotalSizeZ = 10.24*m;
+  fFASER2TotalSizeZ = 25*m;//10.24*m;
path: src/geometry/GeometricalParameters.cc
user: login: mvicenzi
body: As mentioned elsewhere, this change requires updating the positioning in the various configuration files.
created_at: 2025-03-25T17:04:29Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+# This file defines a simplified configuration for the FPF reference hall
+# The geometry contains:
+# - FASER2 (SAMURAI magnet)
+
+/det/fileGdml FASER2_only.gdml
+
+# Configuring FLArE
+/det/addFLArE false
+/det/flare/addFLArEPos 0 0 4300 mm
+/det/flare/material LAr
+/det/flare/module 3x7
+/det/flare/field 1. tesla
+/det/flare/useBabyMIND false
+
+# Configuring FORMOSA
+# assuming 1.2m gap from FLArE
+/det/addFORMOSA false
+/det/formosa/addFORMOSAPos 0 0 13870 mm
+
+# Configuring FASERnu2
+# assuming 1.2m gap from FORMOSA
+/det/addFASERnu2 false
+/det/fasernu/addFASERnu2Pos 0 0 22023 mm
+
+# Configuring FASER2
+# assuming 1.2m gap from FASERnu2
+# (which include veto stations)
+# + 10m decay tunnel
+/det/addFASER2 true
+/det/faser/addFASER2Pos 0 0 36031 mm
path: macros/geometry_options/FASER2_CP_only.mac
user: login: mvicenzi
body: This needs to be changed since the center of the assembly is no longer the magnet center, as well as many other lengths having changed. I can help figuring out what to set here.
created_at: 2025-03-25T17:09:45Z
author_association: COLLABORATOR
diff_hunk: @@ -29,6 +29,7 @@ class HepMCGeneratorMessenger: public G4UImessenger
     G4UIcmdWithAString* fHepMCInputFileCmd;
     G4UIcmdWith3VectorAndUnit* fHepMCVertexOffsetCmd;
     G4UIcmdWithABool* fUseHepMC2Cmd;
+    G4UIcmdWithABool* fHepMCPlaceInDecayVolume;
path: include/generators/HepMCGeneratorMessenger.hh
user: login: benw22022
body: Done 
created_at: 2025-03-26T10:32:32Z
author_association: COLLABORATOR
diff_hunk: @@ -88,6 +102,39 @@ void HepMCGenerator::GeneratePrimaries(G4Event* anEvent)
 }
 
 
+static G4double GetStartOfDecayVolume()
+{
+    G4VPhysicalVolume* DVphysicalVolume = FindPhysicalVolumeByName("FASER2DecayVolPhysical");
path: src/generators/HepMCGenerator.cc
user: login: benw22022
body: My thinking was that the members of `GeometricalParameters` are more likely to change than the names of the physical volumes and it makes the calculation less dependent on the rest of the FASER2 geometry - but tbf you're right and that might quite slow if we're calling this function every event. We could just call it once and save the value - but that could break if the geometry were to change afterwards. I'll change my code to use `GeometricalParameters` to be safe
created_at: 2025-03-26T15:16:32Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
+
+  //* ------------ Place components in order that they appear ------------ *// 
+  
+  G4double componentPosition = -totLengthZ/2; //* We'll increment this variable as we move through the detector to place things
path: src/geometry/FASER2DetectorConstruction.cc
user: login: benw22022
body: Ahhh yes - good point - although isn't the gap between detectors though dependent then on their length? i.e. if I increase the length of the magnet then I would need to change the offset in the macro file too in order to maintain the same distance between detectors? 

I was wondering if a better idea would be if we remove the commands like 
`/det/faser/addFASER2Pos 0 0 36031 mm` and replace them with something like `/det/faser/offset 0 0 1.5 m` - which would place the detector assembly 1.5 m from the last detector (or from the front of the hall if it is the 1st) - the absolute positions of the detector for the `GV4Placement` we could get by constructing them so that their lengths in `GeometricalParameters` are updated and then using that to compute the offset at runtime?
created_at: 2025-03-26T15:49:19Z
author_association: COLLABORATOR
diff_hunk: @@ -71,8 +72,9 @@ class DetectorConstruction : public G4VUserDetectorConstruction {
 
     // FASER2
     G4LogicalVolume* FASER2MagnetLogical;
-    G4LogicalVolume* TrackingVerScinBarLogical;
-    G4LogicalVolume* TrackingHorScinBarLogical; 
+    std::vector<G4LogicalVolume*> FASER2TrackingLogical;
path: include/DetectorConstruction.hh
user: login: benw22022
body: This is probably me not understanding how to use GEANT4 properly 😅 - my idea was that in doing so you could set the names of each SD individually - which I thought would could useful. I do this in my other GEANT4 project (for the SCT based FASERv run 4 detector concept) and it means that I get a hits collection for each SD. By looping through the HC I can then assign an index to represent the layer of the hit for each (like I do [here](https://github.com/benw22022/FASERvSi_G4/blob/master/FASERvSi_baseline/src/EventAction.cc#L146)). For certain tasks having an integer for the layer number is more useful than a double for the the z position (which has some variation which can make some jobs a bit fiddly - I was finding that some hits in the same layer had slightly different z-positions which made grouping them by layer harder) 

But I'm still pretty new to GEANT4 - if I'm being daft here I'm happy to change this!
created_at: 2025-03-26T16:02:28Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
+
+  //* ------------ Place components in order that they appear ------------ *// 
+  
+  G4double componentPosition = -totLengthZ/2; //* We'll increment this variable as we move through the detector to place things
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Yes, that's how it is currently working (and it's not great). It gives great flexibility (you can place detectors in any order, anywhere), but it's not practical. 

Your idea definitely works better. It will just force a specific detector order, so support for some of the alternative options will need to be dropped. But given that the big picture geometry is basically fixed now, I'd say it's not that important anymore to keep all this flexibility.
My only question: what happens if a detector is turned off? For example, if FASERnu2 is not built, we don't currently update its length in `GeometricalParameters`, but you might still want FASER2 to be where it should. Perhaps we could always build all detectors, and only wrap the `GV4Placement` statements inside the `if` conditions?

It's probably better to avoid expanding too much the scope this PR... Let's close the FASER2-only changes, and then revise the general geometry implementation in the context of #53 .
For now, what you could do is run the code, look at the `G4cout` printous that give final size and position of all detectors and adjust the offset(s) in the configuration files manually to be where you like.
created_at: 2025-03-26T16:55:56Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
+
+  //* ------------ Place components in order that they appear ------------ *// 
+  
+  G4double componentPosition = -totLengthZ/2; //* We'll increment this variable as we move through the detector to place things
path: src/geometry/FASER2DetectorConstruction.cc
user: login: benw22022
body: I'll update the the detector offsets in the macro files using the current setup, I agree that changing how the detectors are positioned is well beyond the scope of this PR. After we close this PR I can think about how we could better manage the detector placement 
created_at: 2025-03-26T17:01:02Z
author_association: COLLABORATOR
diff_hunk: @@ -71,8 +72,9 @@ class DetectorConstruction : public G4VUserDetectorConstruction {
 
     // FASER2
     G4LogicalVolume* FASER2MagnetLogical;
-    G4LogicalVolume* TrackingVerScinBarLogical;
-    G4LogicalVolume* TrackingHorScinBarLogical; 
+    std::vector<G4LogicalVolume*> FASER2TrackingLogical;
path: include/DetectorConstruction.hh
user: login: mvicenzi
body: Ahh, I think I see now what you are doing... 😅 
My confusion came from the fact that I was looking at `BuildTrackingStation()` instead of the main construction loop. These github diffs are tricky sometimes, sorry...

In the code you linked (and in the main loop), you are building the logical volumes in a loop so they are all distinct objects (with also a different name, but that's not essential). This means they get registered as different volumes (despite sharing the same properties). Instead, in the leftover (?) `BuildTrackingStation()`, you were using the same `trkLayerLogical` object for all layers... so my guess was that it wouldn't recognize them as different because they were not distinct objects. 

I retract my objection then! 

I still think it's not memory efficient (wouln't scale well for all the FASER2nu layers, for instance), but it's okay for just ~6 volumes. I believe the memory efficient solution is to compute the layer ID directly when filling the HitCollection with your hits: you add your "volume_id" variables in the hit structure and have the SensitiveDetector directly fill them while storing the hits. At that point you have access to the `G4Step`, so both the physical/logical voumes and you can use their copynumbers or similar (ideally something that's also in the GDML).
created_at: 2025-03-26T17:42:50Z
author_association: COLLABORATOR
diff_hunk: @@ -360,16 +361,25 @@ void DetectorConstruction::ConstructSDandField() {
   }
 
   if (m_addFASER2) {
-    LArBoxSD* TrkHorScinSD = new LArBoxSD("TrkHorScinSD");
-    TrackingHorScinBarLogical->SetSensitiveDetector(TrkHorScinSD);
-    sdManager->AddNewDetector(TrkHorScinSD);
-    GeometricalParameters::Get()->AddSD2List(SDIdx, "TrkHorScinSD/lar_box");
+    for (auto& station: FASER2TrackingLogical)
+    {
+      LArBoxSD* TrkScinSD = new LArBoxSD("FASER2TrackerSD"+ std::to_string(SDIdx));
+      station->SetSensitiveDetector(TrkScinSD);
+      sdManager->AddNewDetector(TrkScinSD);
+      GeometricalParameters::Get()->AddSD2List(SDIdx, "FASER2TrackerSD"+ std::to_string(SDIdx)+"/lar_box");
+      SDIdx++;
+    }
path: src/DetectorConstruction.cc
user: login: mvicenzi
body: Retracting this as discussed above. I was looking in the wrong place. These are not indentical objects, so it does work. Can be improved for memory effiency, but it's not vital for just ~6 volumes.
created_at: 2025-03-26T17:59:40Z
author_association: COLLABORATOR
diff_hunk: @@ -201,32 +288,19 @@ void FASER2DetectorConstruction::BuildCrystalPullingDesign()
 
 void FASER2DetectorConstruction::BuildTrackingStation()
 {
-  // Each tracking station is made of 2 layers
-  // 1st layer: fNScinBarsY horizontal modules
-  // 2nd layer: fNScinBarsX vertical modules
-  G4double horizontalScinSize = fTrackingStationY / fNScinBarsY; // y size of horizontal scin
-  G4double verticalScinSize = fTrackingStationX / fNScinBarsX; // x size of vertical scin
-
   // layer volume: same size, but first has horizontal bars, second one has vertical bars
   auto trkLayerSolid = new G4Box("trkLayerBox", fTrackingStationX/2, fTrackingStationY/2., fScinThickness/2.);
-  auto trkHorLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkHorLayerLogical"); 
-  auto trkVerLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkVerLayerLogical"); 
-  
-  // first layer is made of 2 horizontal pieces
-  auto trkHorScinSolid = new G4Box("trkHorScinSolid", fTrackingStationX/2., horizontalScinSize/2., fScinThickness/2.);
-  fHorTrackingScinBar = new G4LogicalVolume(trkHorScinSolid, fMaterials->Material("Polystyrene"), "trkHorScinLogical");
-  new G4PVReplica("trkHorScinPhysical", fHorTrackingScinBar,
-                  trkHorLayerLogical, kYAxis, fNScinBarsY, horizontalScinSize);
-  // secondo layer is made of 7 vertical pieces
-  auto trkVerScinSolid = new G4Box("trkHorScinSolid", verticalScinSize/2., fTrackingStationY/2., fScinThickness/2.);
-  fVerTrackingScinBar = new G4LogicalVolume(trkVerScinSolid, fMaterials->Material("Polystyrene"), "trkVerScinLogical");
-  new G4PVReplica("trkVerScinPhysical", fVerTrackingScinBar,
-                  trkVerLayerLogical, kXAxis, fNScinBarsX, verticalScinSize);
-  
-  fTrackingStation = new G4AssemblyVolume(); //one assembly has 2 layers
+  auto trkLayerLogical = new G4LogicalVolume(trkLayerSolid, fMaterials->Material("Polystyrene"), "trkLayerLogical");
+  
+  G4VisAttributes* stationVis = new G4VisAttributes(G4Colour(34./255, 148./255, 83./255, 0.8));
+  stationVis->SetVisibility(true);
+  trkLayerLogical->SetVisAttributes(stationVis);
+
+  fTrackingStation = new G4AssemblyVolume();
   G4RotationMatrix rot(0, 0, 0);
   G4ThreeVector pos(0, 0, -fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkHorLayerLogical,pos,&rot);
   pos.setZ(fScinThickness/2.);
-  fTrackingStation->AddPlacedVolume(trkVerLayerLogical,pos,&rot);
+  auto trkLayerPhysical = new G4PVPlacement(0, pos, trkLayerLogical, "TrkPhys", fFASER2Assembly, false, 0);
+  fTrackingStation->AddPlacedVolume(trkLayerLogical,pos,&rot);
+  fTrackingStationsLogical.push_back(trkLayerLogical);
path: src/geometry/FASER2DetectorConstruction.cc
user: login: mvicenzi
body: Retracting this as `BuildTrackingStations()` is no longer used... please remove the function completely!
created_at: 2025-03-26T17:59:53Z
author_association: COLLABORATOR
diff_hunk: @@ -21,152 +21,239 @@ FASER2DetectorConstruction::FASER2DetectorConstruction()
 
   // choose magnet option
   GeometricalParameters::magnetOption opt = GeometricalParameters::Get()->GetFASER2MagnetOption(); 
-  if( opt == GeometricalParameters::magnetOption::SAMURAI ){
-    
-    G4cout << "Building SAMURAI spectrometer magnet" << G4endl;
-    fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
-    fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
-    fMagnetWindowZ = GeometricalParameters::Get()->GetFASER2MagnetWindowZ();
-    fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
-    fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
-   
-    fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
-    fTrackingStationX = fMagnetWindowX + 0.5*m; //match magnet size + bending plane
-    fTrackingStationY = fMagnetWindowY; // match magnet size
-    fNScinBarsY = GeometricalParameters::Get()->GetNScintillatorBarsY();
-    fNScinBarsX = GeometricalParameters::Get()->GetNScintillatorBarsX();
-    fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
-    fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
-    G4double gapToMagnet = fTrackingStationGap;
-    G4double stationThickness = 2*fScinThickness;
-    G4double totThickness = fNTrackingStations*stationThickness + (fNTrackingStations-1)*fTrackingStationGap;
-    G4double totLengthZ = fMagnetWindowZ+2*gapToMagnet+2*totThickness;
-    G4double totLengthX = fMagnetWindowX+2*fMagnetYokeThicknessX;
-    G4double totLengthY = fMagnetWindowY+2*fMagnetYokeThicknessY;
-
-    GeometricalParameters::Get()->SetMagnetTotalSizeZ(fMagnetWindowZ);
-    GeometricalParameters::Get()->SetTrackingStationTotalSizeZ(totThickness);
-    GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
 
-    BuildSAMURAIDesign(); //sets logical volumes
-    BuildTrackingStation(); //sets assembly volume
-    
-    auto containerSolid = new G4Box("FASER2Solid", totLengthX/2., totLengthY/2., totLengthZ/2.);
-    fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+  fMagnetWindowX = GeometricalParameters::Get()->GetFASER2MagnetWindowX();
+  fMagnetWindowY = GeometricalParameters::Get()->GetFASER2MagnetWindowY();
+  fMagnetYokeThicknessX = GeometricalParameters::Get()->GetFASER2MagnetYokeThickX();
+  fMagnetYokeThicknessY = GeometricalParameters::Get()->GetFASER2MagnetYokeThickY();
 
-    // center of the assembly is the center of the magnet window
-    G4ThreeVector magCenter(0.,0.,0.);
-    G4RotationMatrix *noRot = new G4RotationMatrix();
+  //? I think it is potentially a bit confusing that we have two magnet Z length variables - I think one would suffice - but I've tried to maintain consistency here for the time being
+  fMagnetLengthZ = (opt == GeometricalParameters::magnetOption::SAMURAI) ? GeometricalParameters::Get()->GetFASER2MagnetWindowZ() : GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
+  fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
+  fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
+  fNMagnets = (opt == GeometricalParameters::magnetOption::SAMURAI) ? 1 : GeometricalParameters::Get()->GetNFASER2Magnets();
+  fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
 
-    // placing magnet + yoke
-    new G4PVPlacement(noRot, magCenter, fMagnetWindow, "FASER2MagnetWindowPhysical", fFASER2Assembly, false, 0, false);
-    new G4PVPlacement(noRot, magCenter, fMagnetYoke, "FASER2MagnetYokePhysical", fFASER2Assembly, false, 0, false);
-
-    // middle position of pre-magnet and post-magnet tracking stations
-    G4ThreeVector offset(0, 0, fMagnetWindowZ/2.+ gapToMagnet + totThickness/2.);
-    G4ThreeVector preStationsCenter = magCenter - offset;
-    G4ThreeVector postStationsCenter = magCenter + offset;
-  
-    // placing tracking stations (before/after magnet)
-    for (int i= 0; i<fNTrackingStations; ++i) { 
-      G4ThreeVector T(0, 0, -totThickness/2.+0.5*stationThickness+i*(fTrackingStationGap+stationThickness));
-      G4ThreeVector Tp = postStationsCenter + T;
-      G4ThreeVector Tm = preStationsCenter + T;
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tm, noRot, 0, false); //place before magnet
-      fTrackingStation->MakeImprint(fFASER2Assembly, Tp, noRot, 0, false); //place after magnet
-    }
+  fDecayVolumeLength = GeometricalParameters::Get()->GetfFASER2DecayVolumeLength();
+  
+  fNTrackingStations = GeometricalParameters::Get()->GetNTrackingStations();
+  fTrackingStationX = GeometricalParameters::Get()->GetFASER2TrackerX();
+  fTrackingStationY = GeometricalParameters::Get()->GetFASER2TrackerY();
+  fScinThickness = GeometricalParameters::Get()->GetScintillatorThickness();
+  fTrackingStationGap = GeometricalParameters::Get()->GetTrackingStationGap();
+  
+  //TODO Implement these in the DetectorConstructionMessenger
+  fUpstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2UpstreamTrackingStationGap();
+  fDownstreamTrackingStationGap = GeometricalParameters::Get()->GetFASER2DownstreamTrackingStationGap();
+  fNUpstreamTrackers = GeometricalParameters::Get()->GetFASER2NUpstreamTrackers();
+  fNDownstreamTrackers = GeometricalParameters::Get()->GetFASER2NDownstreamTrackers();
 
-  } else if ( opt == GeometricalParameters::magnetOption::CrystalPulling ){
- 
-    G4cout << "Building CrystalPulling spectrometer magnet" << G4endl;
-    fMagnetLengthZ = GeometricalParameters::Get()->GetFASER2MagnetLengthZ();
-    fMagnetInnerR = GeometricalParameters::Get()->GetFASER2MagnetInnerR();
-    fMagnetOuterR = GeometricalParameters::Get()->GetFASER2MagnetOuterR();
-    fNMagnets = GeometricalParameters::Get()->GetNFASER2Magnets();
-    fMagnetGap = GeometricalParameters::Get()->GetFASER2MagnetGap();
+  fEMCaloThickness = GeometricalParameters::Get()->GetFASER2EMCaloThickness();
+  fHadCaloThickness = GeometricalParameters::Get()->GetFASER2HadCaloThickness();
+  fIronWallThickness = GeometricalParameters::Get()->GetFASER2IronWallThickness();
+
+  fVetoLengthX = GeometricalParameters::Get()->GetFASER2VetoLengthX();
+  fVetoLengthY = GeometricalParameters::Get()->GetFASER2VetoLengthY();
+  fVetoShieldThickness = GeometricalParameters::Get()->GetFASER2VetoShieldThickness();
+
+  //* Work out the maximum extent of the whole of the FASER2 detector so we can make the physical volume
+  //* We want to avoid any overlaps as this can be problematic for GENIE when it reads the GDML file
+  G4double vetoLength = 2*fScinThickness + fVetoShieldThickness;
+  G4double upstreamTrackerLength = fNUpstreamTrackers*fScinThickness + (fNUpstreamTrackers - 1)*fTrackingStationGap + fUpstreamTrackingStationGap;
+  G4double magnetLength = fNMagnets*fMagnetLengthZ + (fNMagnets-1)*fMagnetGap;
+  G4double downstreamTrackerLength = fNDownstreamTrackers*fScinThickness + (fNDownstreamTrackers - 1)*fTrackingStationGap + fDownstreamTrackingStationGap;
+  G4double caloLength = fTrackingStationGap+fEMCaloThickness+fHadCaloThickness+fIronWallThickness;
+  G4double muonLength = fScinThickness+fTrackingStationGap+0.5*m;
+  
+  G4double totLengthZ =  vetoLength + fDecayVolumeLength +  upstreamTrackerLength + magnetLength + downstreamTrackerLength + caloLength + muonLength;
+  
+  std::vector<G4double> lengths{fMagnetWindowX+2*fMagnetYokeThicknessX, fVetoLengthX, fMagnetOuterR};
+  std::vector<G4double> heights{fMagnetWindowY+2*fMagnetYokeThicknessY, fVetoLengthY, fMagnetOuterR};
+  
+  auto totLengthX = std::max_element(lengths.begin(), lengths.end());
+  auto totLengthY = std::max_element(heights.begin(), heights.end());
+
+  GeometricalParameters::Get()->SetFASER2TotalSizeZ(totLengthZ);
+  G4RotationMatrix *noRot = new G4RotationMatrix();
+  
+  //* Create the FASER2 volume - all components go in here
+  auto containerSolid = new G4Box("FASER2Solid", *totLengthX/2., *totLengthY/2., totLengthZ/2.);
+  fFASER2Assembly = new G4LogicalVolume(containerSolid, fMaterials->Material("Air"), "FASER2Logical");
+
+  G4VisAttributes* F2_logVisAtt = new G4VisAttributes(G4Colour::Grey());
+  F2_logVisAtt->SetForceWireframe(true);
+  F2_logVisAtt->SetForceSolid(false);
+  F2_logVisAtt->SetVisibility(true);
+  fFASER2Assembly->SetVisAttributes(F2_logVisAtt);
path: src/geometry/FASER2DetectorConstruction.cc
user: login: benw22022
body: Moved the visualisation code into it's own function
created_at: 2025-03-27T10:39:11Z
author_association: COLLABORATOR
diff_hunk: @@ -71,8 +72,9 @@ class DetectorConstruction : public G4VUserDetectorConstruction {
 
     // FASER2
     G4LogicalVolume* FASER2MagnetLogical;
-    G4LogicalVolume* TrackingVerScinBarLogical;
-    G4LogicalVolume* TrackingHorScinBarLogical; 
+    std::vector<G4LogicalVolume*> FASER2TrackingLogical;
path: include/DetectorConstruction.hh
user: login: benw22022
body: Thanks! I'll leave it how it is then for now and save the updating of the HitCollection with the copy numbers for a future PR
created_at: 2025-03-27T10:44:08Z
author_association: COLLABORATOR
diff_hunk: @@ -101,8 +101,7 @@ class DetectorConstructionMessenger: public G4UImessenger {
     G4UIcmdWithAnInteger* faserMagnetNumberCmd;
     // Tracking stations
     G4UIcmdWithAnInteger* faserTrackingNumberCmd;
path: include/DetectorConstructionMessenger.hh
user: login: benw22022
body: Thinking about it a bit more - I think having separate numbers of stations for up/downstream trackers is overkill - I'm not sure that we actually need it. I'll revert this back to how it was to keep things simple
created_at: 2025-03-27T11:08:49Z
author_association: COLLABORATOR
diff_hunk: @@ -68,12 +69,22 @@ GeometricalParameters::GeometricalParameters()
   fFASER2MagnetGap = 0.25*m;
   fFASER2MagnetSpacing = 0.76*m; //updates during construction
   // Tracking stations
-  fNTrackingStations = 6;
-  fNScintillatorBarsY = 3;
-  fNScintillatorBarsX = 7;
+  fNTrackingStations = 3;
path: src/geometry/GeometricalParameters.cc
user: login: benw22022
body: Decided to revert back to just the one number for the tracking stations
created_at: 2025-03-27T11:15:12Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+# This file defines a simplified configuration for the FPF reference hall
+# The geometry contains:
+# - FASER2 (SAMURAI magnet)
+
+/det/fileGdml FASER2_only.gdml
+
+# Configuring FLArE
+/det/addFLArE false
+/det/flare/addFLArEPos 0 0 4300 mm
+/det/flare/material LAr
+/det/flare/module 3x7
+/det/flare/field 1. tesla
+/det/flare/useBabyMIND false
+
+# Configuring FORMOSA
+# assuming 1.2m gap from FLArE
+/det/addFORMOSA false
+/det/formosa/addFORMOSAPos 0 0 13870 mm
+
+# Configuring FASERnu2
+# assuming 1.2m gap from FORMOSA
+/det/addFASERnu2 false
+/det/fasernu/addFASERnu2Pos 0 0 22023 mm
+
+# Configuring FASER2
+# assuming 1.2m gap from FASERnu2
+# (which include veto stations)
+# + 10m decay tunnel
+/det/addFASER2 true
+/det/faser/addFASER2Pos 0 0 36031 mm
path: macros/geometry_options/FASER2_CP_only.mac
user: login: benw22022
body: Should be fixed now
created_at: 2025-03-27T13:42:50Z
author_association: COLLABORATOR
diff_hunk: @@ -53,27 +53,36 @@ GeometricalParameters::GeometricalParameters()
   fMagnetTotalSizeZ = 4*m; //updates during construction
   fTrackingStationTotalSizeZ = 2.62*m; //updates during construction
   fMagnetZPos = 40*m; //updates during construction
-  fFASER2TotalSizeZ = 10.24*m;
+  fFASER2TotalSizeZ = 25*m; //updates during construction
+  fFASER2DecayVolumeLength = 10*m;
   // SAMURAI design
   fFASER2MagnetWindowX = 3.0*m;
   fFASER2MagnetWindowY = 1.0*m;
   fFASER2MagnetWindowZ = 4.0*m;
   fFASER2MagnetYokeThickX = 1.5*m;
-  fFASER2MagnetYokeThickY = 2.0*m;
+  fFASER2MagnetYokeThickY = 0.5*m;
   // CrystalPulling design
-  fFASER2MagnetLengthZ = 1.25*m;
   fFASER2MagnetInnerR = 0.8*m;
   fFASER2MagnetOuterR = 1.2*m;
   fNFASER2Magnets = 3;
   fFASER2MagnetGap = 0.25*m;
   fFASER2MagnetSpacing = 0.76*m; //updates during construction
   // Tracking stations
-  fNTrackingStations = 6;
-  fNScintillatorBarsY = 3;
-  fNScintillatorBarsX = 7;
+  fNTrackingStations = 3;
   fScintillatorThickness = 1*cm;
   fTrackingStationGap = 0.5*m;
   fFASER2Pos = G4ThreeVector(0., 0., 42636.*mm);
+  fFASER2TrackerX = 3*m;
+  fFASER2TrackerY = 1*m;  
+  fFASER2UpstreamTrackingStationGap = 0.5*m;
+  fFASER2DownstreamTrackingStationGap = 4*m;
+
+  fFASER2EMCaloThickness = 1*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2HadCaloThickness = 2*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2IronWallThickness = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthX = 4*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthY = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoShieldThickness = 2.5*cm; //! These values are placeholders and require dedicated optmisation studies!
path: src/geometry/GeometricalParameters.cc
user: login: OlivierSalin
body: Parameters values looks fine to me !  At least at this stage of the FASER2 design.
I am just wondering about the Veto dimension: 
```
 fFASER2VetoLengthX = 4*m
 fFASER2VetoLengthY = 3*m
fFASER2VetoShieldThickness = 2.5*cm;
```
 I think the Veto should have similar dimension as the FASER2 decay volume so X=3m and Y=1m 
 Regarding the thickness of the Veto shield, I don't recall seing a specific value so 2.5 cm extrapolated from FASER (I guess) seems correct to me.
created_at: 2025-03-27T17:27:17Z
author_association: COLLABORATOR
diff_hunk: @@ -34,9 +34,7 @@
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
path: macros/geometry_options/FASER2_only.mac
user: login: OlivierSalin
body: This might be quite naive from me and maybe outside of the scope of this PR. To me this way of manually updating the parameters for each of the geometry options might be quite error prone for futur users and leed to inconsistancy. Would it be possible to have parameters with auto updates used in all geometry options ?
What do you think @mvicenzi ? 
created_at: 2025-03-27T17:40:04Z
author_association: COLLABORATOR
diff_hunk: @@ -34,9 +34,7 @@
 /det/faser/magnetWinY 1.0 m
 /det/faser/magnetWinZ 4.0 m
 /det/faser/yokeThickX 1.5 m
-/det/faser/yokeThickY 2.0 m
-/det/faser/trackingNumber 6
+/det/faser/yokeThickY 1.0 m
+/det/faser/trackingNumber 3
 /det/faser/trackingGap 0.5 m
-/det/faser/trackingNBarsX 7
-/det/faser/trackingNBarsY 7
 /det/faser/trackingScinThick 1.0 cm
path: macros/geometry_options/FASER2_only.mac
user: login: mvicenzi
body: As it is currently setup, if the configuration parameters are not explicitly set in the `.mac` files, the code uses the default values hard-coded in the constructor of `GeometricalParameters`. You always have the option to override them in the macro, it's not necessary to explicitly set them. Most likely we don't need to expose all these FASER2 values, so it's fine for me to remove them.

Apart from that, an additional layer of consistency is given by the fact that users should be encouranged to just "import" the standard geometries stored in `macros/geometry_options` (using `/control/execute \path\to\geometry.mac`) so that they don't have to set things themselves.
created_at: 2025-03-27T19:53:56Z
author_association: COLLABORATOR
diff_hunk: @@ -53,27 +53,36 @@ GeometricalParameters::GeometricalParameters()
   fMagnetTotalSizeZ = 4*m; //updates during construction
   fTrackingStationTotalSizeZ = 2.62*m; //updates during construction
   fMagnetZPos = 40*m; //updates during construction
-  fFASER2TotalSizeZ = 10.24*m;
+  fFASER2TotalSizeZ = 25*m; //updates during construction
+  fFASER2DecayVolumeLength = 10*m;
   // SAMURAI design
   fFASER2MagnetWindowX = 3.0*m;
   fFASER2MagnetWindowY = 1.0*m;
   fFASER2MagnetWindowZ = 4.0*m;
   fFASER2MagnetYokeThickX = 1.5*m;
-  fFASER2MagnetYokeThickY = 2.0*m;
+  fFASER2MagnetYokeThickY = 0.5*m;
   // CrystalPulling design
-  fFASER2MagnetLengthZ = 1.25*m;
   fFASER2MagnetInnerR = 0.8*m;
   fFASER2MagnetOuterR = 1.2*m;
   fNFASER2Magnets = 3;
   fFASER2MagnetGap = 0.25*m;
   fFASER2MagnetSpacing = 0.76*m; //updates during construction
   // Tracking stations
-  fNTrackingStations = 6;
-  fNScintillatorBarsY = 3;
-  fNScintillatorBarsX = 7;
+  fNTrackingStations = 3;
   fScintillatorThickness = 1*cm;
   fTrackingStationGap = 0.5*m;
   fFASER2Pos = G4ThreeVector(0., 0., 42636.*mm);
+  fFASER2TrackerX = 3*m;
+  fFASER2TrackerY = 1*m;  
+  fFASER2UpstreamTrackingStationGap = 0.5*m;
+  fFASER2DownstreamTrackingStationGap = 4*m;
+
+  fFASER2EMCaloThickness = 1*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2HadCaloThickness = 2*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2IronWallThickness = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthX = 4*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoLengthY = 3*m; //! These values are placeholders and require dedicated optmisation studies!
+  fFASER2VetoShieldThickness = 2.5*cm; //! These values are placeholders and require dedicated optmisation studies!
path: src/geometry/GeometricalParameters.cc
user: login: benw22022
body: Thanks for review @OlivierSalin!! The veto dimensions were a bit of a guess - I thought that 2.5cm, which is roughly 5 X0 would be an ok starting point. I hadn't thought to check the FASER veto thickness - which turns out to be 10 cm of lead (according to this [doc](https://cds.cern.ch/record/2816452/files/FASER_Detector_Paper_22.7.22.pdf)). I'll increase the thickness of the veto shield to match this. 
W.r.t the veto X-Y dimensions - I was thinking that you'd want the veto to be a bit bigger than the tracking planes so that you can reject more of the geometric muon background?
created_at: 2025-03-28T10:10:48Z
author_association: COLLABORATOR

PR comments:
user: login: benw22022
created_at: 2025-03-25T17:31:35Z
body: Thanks for the review @mvicenzi! Your suggestions look very reasonable - I'll try and address each of these one by one this week 
author_association: COLLABORATOR
user: login: OlivierSalin
created_at: 2025-03-27T18:06:42Z
body: Apologies for the delays, many thanks @benw22022  for those updates, it is awesome. It all seems great to me.

I double checked all the parameters, all of them seems to make sense to me and consistant with the current FASER2 design (expect the Veto dimension). Those values are yet to be properly optimised (especially calo) which is why they are not documented yet.

author_association: COLLABORATOR
user: login: benw22022
created_at: 2025-03-28T11:11:42Z
body: Thanks @mvicenzi and @OlivierSalin, I think this is complete now - I will go ahead an merge. Thanks again for all the feedback!
author_association: COLLABORATOR