Skip to content
Snippets Groups Projects
create_psdgeo_52modules.C 37.82 KiB
/** @file create_psdgeo_with_hole.C
 ** @author Volker Friese <v.friese@gsi.de>
 ** @date 16 September 2017
 ** @version 1.0
 **
 ** This macro creates a PSD geometry in ROOT format to be used as input
 ** for the transport simulation. It allows to create module stacks of
 ** varying sizes; the numbers of rows and columns must both be uneven.
 ** The centre module as well as the edge modules are omitted.
 ** The module definition is implemented in the function ConstructModule.
 **
 ** The user can adjust the steering variables to the specific needs.
 **/

#include <iostream>


using std::cout;
using std::endl;
using std::fstream;
using std::right;
using std::setw;

// Forward declarations
TGeoVolume* ConstructModule(const char* name,
                            Double_t moduleSize,
                            Int_t nLayers,
                            fstream* info = 0);

TGeoVolume* ConstructModuleWithHole(const char* name,
                                    Double_t moduleSize,
                                    Int_t nLayers,
                                    fstream* info  = 0,
                                    float holesize = 20,
                                    Int_t hole_pos = 0);

TGeoVolume* ConstructShield(const char* name,
                            Double_t sizeXY,
                            Double_t holesize,
                            Int_t hole_pos,
                            fstream* info);

// ============================================================================
// ======                         Main function                           =====
// ============================================================================

void create_psdgeo_52modules() {
  // -----   Steering variables   ---------------------------------------------
  const Double_t psdX = 9.65;  // x position of PSD in cave (front plane center)
  const Double_t psdY = 0.;    // y position of PSD in cave (front plane center)
  const Double_t psdZ = 800.;  // z position of PSD in cave (front plane center)
  const Double_t psdRotY = 0.01321;  // Rotation of PSD around y axis [rad]

  const Double_t bigModuleSize   = 20.;  // Module size [cm]
  const Double_t smallModuleSize = 10.;  // Module size [cm]
  const Int_t nModulesX = 8;   // Number of modules in a row (x direction)
  const Int_t nModulesY = 6;   // Number of modules in a row (x direction)
  const Int_t nLayers   = 60;  // Number of sections in a module (z direction)

  TString geoTag = Form("52mod_hole20cm_xshift%4.2fcm", psdX);  // Geometry tag

  const Double_t shieldWidth = 16.;
  // --------------------------------------------------------------------------

  // ---- Number of modules per row/column must be uneven ---------------------
  // Otherwise, the central one (to be skipped) is not defined.
  //   if ( nModulesX % 2 != 1  || nModulesY % 2 != 1 ) {
  cout << "Number of modules per row and column = " << nModulesX << " "
       << nModulesY << endl;
  //     return;
  //   }
  // --------------------------------------------------------------------------


  // -------   Open info file   -----------------------------------------------
  TString infoFileName = "psd_";
  infoFileName         = infoFileName + geoTag + ".geo.info";
  fstream infoFile;
  infoFile.open(infoFileName.Data(), fstream::out);
  infoFile << "PSD geometry " << geoTag
           << " created with create_psdgeo_52modules.C" << endl
           << endl;
  infoFile << "Number of modules: " << nModulesX << " x " << nModulesY << endl;
  infoFile << "Small module size: " << smallModuleSize << " cm x "
           << smallModuleSize << " cm" << endl;
  infoFile << "Big module size: " << bigModuleSize << " cm x " << bigModuleSize
           << " cm" << endl;
  infoFile << "PSD front plane center coordinates: (" << psdX << ", " << psdY
           << ", " << psdZ << ") cm" << endl;
  infoFile << "PSD rotation around y axis: " << psdRotY * TMath::RadToDeg()
           << " degrees" << endl;
  infoFile << "Size of the square shaped hole in PSD center: 20 cm" << endl
           << endl;
  // --------------------------------------------------------------------------


  // -------   Load media from media file   -----------------------------------
  cout << endl << "==> Reading media..." << endl;
  FairGeoLoader* geoLoad    = new FairGeoLoader("TGeo", "FairGeoLoader");
  FairGeoInterface* geoFace = geoLoad->getGeoInterface();
  TString geoPath           = gSystem->Getenv("VMCWORKDIR");
  TString medFile           = geoPath + "/geometry/media.geo";
  geoFace->setMediaFile(medFile);
  geoFace->readMedia();
  // --------------------------------------------------------------------------


  // ------   Create the required media from the media file    ----------------
  FairGeoMedia* geoMedia   = geoFace->getMedia();
  FairGeoBuilder* geoBuild = geoLoad->getGeoBuilder();

  // ---> Air
  FairGeoMedium* mAir = geoMedia->getMedium("air");
  if (!mAir) Fatal("Main", "FairMedium air not found");
  geoBuild->createMedium(mAir);
  TGeoMedium* air = gGeoManager->GetMedium("air");
  if (!air) Fatal("Main", "Medium air not found");
  cout << "Created medium: air" << endl;

  // ---> Iron
  FairGeoMedium* mIron = geoMedia->getMedium("iron");
  if (!mIron) Fatal("Main", "FairMedium iron not found");
  geoBuild->createMedium(mIron);
  TGeoMedium* iron = gGeoManager->GetMedium("iron");
  if (!iron) Fatal("Main", "Medium iron not found");
  cout << "Created medium: iron" << endl;

  // ---> Lead
  FairGeoMedium* mLead = geoMedia->getMedium("lead");
  if (!mIron) Fatal("Main", "FairMedium lead not found");
  geoBuild->createMedium(mLead);
  TGeoMedium* lead = gGeoManager->GetMedium("lead");
  if (!lead) Fatal("Main", "Medium iron not found");
  cout << "Created medium: lead" << endl;

  // ---> Tyvek
  FairGeoMedium* mTyvek = geoMedia->getMedium("PsdTyvek");
  if (!mTyvek) Fatal("Main", "FairMedium PsdTyvek not found");
  geoBuild->createMedium(mTyvek);
  TGeoMedium* tyvek = gGeoManager->GetMedium("PsdTyvek");
  if (!tyvek) Fatal("Main", "Medium PsdTyvek not found");
  cout << "Created medium: PsdTyvek" << endl;

  // ---> Scintillator
  FairGeoMedium* mScint = geoMedia->getMedium("PsdScint");
  if (!mScint) Fatal("Main", "FairMedium PsdScint not found");
  geoBuild->createMedium(mScint);
  TGeoMedium* scint = gGeoManager->GetMedium("PsdScint");
  if (!scint) Fatal("Main", "Medium PsdScint not found");
  cout << "Created medium: PsdScint" << endl;

  // ---> Fibres
  FairGeoMedium* mFibre = geoMedia->getMedium("PsdFibre");
  if (!mFibre) Fatal("Main", "FairMedium PsdFibre not found");
  geoBuild->createMedium(mFibre);
  TGeoMedium* fibre = gGeoManager->GetMedium("PsdFibre");
  if (!fibre) Fatal("Main", "Medium PsdFibre not found");
  cout << "Created medium: PsdFibre" << endl;

  // ---> Shield Polyethylene
  FairGeoMedium* mShieldPoly = geoMedia->getMedium("PsdPolyethylene");
  if (!mShieldPoly) Fatal("Main", "FairMedium PsdPolyethylene not found");
  geoBuild->createMedium(mShieldPoly);
  TGeoMedium* ShieldPoly = gGeoManager->GetMedium("PsdPolyethylene");
  if (!ShieldPoly) Fatal("Main", "Medium PsdPolyethylene not found");
  cout << "Created medium: PsdPolyethylene" << endl;


  // ---> Medium for PSD volume
  TGeoMedium* psdMedium = air;
  // --------------------------------------------------------------------------


  // --------------   Create geometry and top volume  -------------------------
  cout << endl << "==> Set top volume..." << endl;
  gGeoManager->SetName("PSDgeom");
  TGeoVolume* top = new TGeoVolumeAssembly("TOP");
  gGeoManager->SetTopVolume(top);
  // --------------------------------------------------------------------------

  // -----   Create the PSD modules   -----------------------------------------
  cout << endl;
  TGeoVolume* module2060 =
    ConstructModule("module2060", bigModuleSize, nLayers, &infoFile);
  TGeoVolume* module1060 =
    ConstructModule("module1060", smallModuleSize, nLayers, &infoFile);
  TGeoVolume* module_shield20 =
    ConstructShield("shield20", bigModuleSize, 0., 0, &infoFile);
  TGeoVolume* module_shield10 =
    ConstructShield("shield10", smallModuleSize, 0., 0, &infoFile);

  //     TGeoVolume* module2060_hole = ConstructModuleWithHole("module2060_hole", bigModuleSize, 60, &infoFile);
  // --------------------------------------------------------------------------


  // ---------------   Create PSD volume   ------------------------------------
  cout << endl;
  cout << "===> Creating PSD...." << endl;

  // --- Determine size of PSD box (half-lengths)
  const Double_t psdSizeX = 0.5 * nModulesX * bigModuleSize;
  const Double_t psdSizeY = 0.5 * nModulesY * bigModuleSize;
  TGeoBBox* shape         = dynamic_cast<TGeoBBox*>(module2060->GetShape());
  const Double_t psdSizeZ = shape->GetDZ();

  TString psdName = "psd_";
  psdName += geoTag;
  TGeoVolume* psd = gGeoManager->MakeBox(
    psdName, psdMedium, psdSizeX, psdSizeY, psdSizeZ + 0.5 * shieldWidth);
  cout << "Module array is " << nModulesX << " x " << nModulesY << endl;
  cout << "PSD size is " << 2. * psdSizeX << " cm x " << 2. * psdSizeY
       << " cm x " << 2. * psdSizeZ << " cm" << endl;
  infoFile << endl
           << "PSD size is " << 2. * psdSizeX << " cm x " << 2. * psdSizeY
           << " cm x " << 2. * psdSizeZ + shieldWidth << " cm" << endl;
  // --------------------------------------------------------------------------


  // -----   Place modules into the system volume   ---------------------------

  Int_t nModules {0};
  Int_t iModule {0};

  const Int_t nSmallModulesX = 4;
  const Int_t nSmallModulesY = 4;
  Double_t xModCurrent       = -0.5 * (nSmallModulesX + 1) * smallModuleSize;
  for (Int_t iModX = 0; iModX < nSmallModulesX; iModX++) {
    xModCurrent += smallModuleSize;
    Double_t yModCurrent = -0.5 * (nSmallModulesY + 1) * smallModuleSize;
    for (Int_t iModY = 0; iModY < nSmallModulesY; iModY++) {
      yModCurrent += smallModuleSize;

      //       remove 4 central modules
      if ((iModX == nSmallModulesX / 2 || iModX == nSmallModulesX / 2 - 1)
          && (iModY == nSmallModulesY / 2 || iModY == nSmallModulesY / 2 - 1))
        continue;

      // Position of module inside PSD
      TGeoTranslation* trans =
        new TGeoTranslation(xModCurrent, yModCurrent, -0.5 * shieldWidth);
      TGeoTranslation* trans_shield =
        new TGeoTranslation(xModCurrent, yModCurrent, psdSizeZ);

      psd->AddNode(module1060, iModule, trans);
      psd->AddNode(module_shield10, iModule, trans_shield);

      cout << "Adding module " << setw(5) << right << iModule
           << " at x = " << setw(6) << right << xModCurrent
           << " cm , y = " << setw(6) << right << yModCurrent << " cm" << endl;
      iModule++;
      nModules++;

    }  //# modules in y direction
  }    //# modules in x direction


  xModCurrent = -0.5 * (nModulesX + 1) * bigModuleSize;
  for (Int_t iModX = 0; iModX < nModulesX; iModX++) {
    xModCurrent += bigModuleSize;
    Double_t yModCurrent = -0.5 * (nModulesY + 1) * bigModuleSize;
    for (Int_t iModY = 0; iModY < nModulesY; iModY++) {
      yModCurrent += bigModuleSize;

      // Skip edge modules
      if ((iModX == 0 || iModX == nModulesX - 1)
          && (iModY == 0 || iModY == nModulesY - 1))
        continue;

      //       Replace 4 central modules with 12 (10 cm x 10 cm) and a (20 cm x 20 cm) hole
      if ((iModX == nModulesX / 2 || iModX == nModulesX / 2 - 1)
          && (iModY == nModulesY / 2 || iModY == nModulesY / 2 - 1))
        continue;

      // Position of module inside PSD
      TGeoTranslation* trans =
        new TGeoTranslation(xModCurrent, yModCurrent, -0.5 * shieldWidth);
      TGeoTranslation* trans_shield =
        new TGeoTranslation(xModCurrent, yModCurrent, psdSizeZ);

      psd->AddNode(module2060, iModule, trans);
      psd->AddNode(module_shield20, iModule, trans_shield);

      cout << "Adding module " << setw(5) << right << iModule
           << " at x = " << setw(6) << right << xModCurrent
           << " cm , y = " << setw(6) << right << yModCurrent << " cm" << endl;
      iModule++;
      nModules++;

    }  //# modules in y direction
  }    //# modules in x direction


  cout << "PSD contains " << nModules << " modules." << endl;
  infoFile << "PSD contains " << nModules << " modules." << endl;
  // --------------------------------------------------------------------------

  // -----   Place PSD in top node (cave) -------------------------------------
  Double_t psdHalfLength = psdSizeZ + 0.5 * shieldWidth;
  Double_t psdVolCenterX = psdX + psdHalfLength * sin(psdRotY);
  Double_t psdVolCenterY = psdY;
  Double_t psdVolCenterZ = psdZ + psdHalfLength * cos(psdRotY);
  infoFile << "PSD volume center coordinates: (" << psdVolCenterX << ", "
           << psdVolCenterY << ", " << psdVolCenterZ << ") cm" << endl;
  TGeoRotation* psdRot = new TGeoRotation();
  psdRot->RotateY(psdRotY * TMath::RadToDeg());
  TGeoCombiTrans* psdTrans =
    new TGeoCombiTrans(psdVolCenterX, psdVolCenterY, psdVolCenterZ, psdRot);
  top->AddNode(psd, 0, psdTrans);
  cout << endl << "==> PSD position in cave: " << endl;
  psdTrans->Print();
  // --------------------------------------------------------------------------


  // -----   Close and check geometry   ---------------------------------------
  cout << endl << "==> Closing geometry..." << endl;
  gGeoManager->CloseGeometry();
  gGeoManager->CheckGeometryFull();
  cout << endl;
  gGeoManager->CheckOverlaps(0.0001, "s");
  // --------------------------------------------------------------------------


  // -----   Write PSD volume and placement matrix to geometry file   ---------
  cout << endl;
  TString geoFileName = "psd_";
  geoFileName         = geoFileName + geoTag + ".geo.root";
  psd->Export(geoFileName);
  TFile* geoFile = new TFile(geoFileName, "UPDATE");
  psdTrans->Write();
  cout << endl;
  cout << "==> Geometry " << psd->GetName() << " written to " << geoFileName
       << endl;
  cout << "==> Info written to " << infoFileName << endl;
  geoFile->Close();
  infoFile.close();
  // --------------------------------------------------------------------------


  // -----   Write entire TGeoManager to file   -------------------------------
  TString geoManFileName = "psd_";
  geoManFileName         = geoManFileName + geoTag + ".geoman.root";
  TFile* geoManFile      = new TFile(geoManFileName, "RECREATE");
  gGeoManager->Write();
  geoManFile->Close();
  cout << "==> TGeoManager " << gGeoManager->GetName() << " written to "
       << geoManFileName << endl
       << endl
       << endl;
  // --------------------------------------------------------------------------

  // ----   Display geometry   ------------------------------------------------
  //     gGeoManager->SetVisLevel(5);  // Scintillator level
  top->Draw("ogl");
  // --------------------------------------------------------------------------
  //     top->Raytrace();
}
//  End of main function
/** ======================================================================= **/

TGeoVolume* ConstructShield(const char* name,
                            Double_t sizeXY,
                            Double_t holesize,
                            Int_t hole_pos,
                            fstream* info) {

  const TString suffix =
    Form("_shield_%d_%d_%d", int(sizeXY), int(holesize), int(hole_pos));

  const Double_t psd2iron     = 2.;
  const Double_t ironD        = 4.;
  const Double_t iron2poly    = 2.;
  const Double_t polyD        = 8.;
  const Double_t shieldLength = psd2iron + ironD + iron2poly + polyD;

  TGeoMedium* medAir = gGeoManager->GetMedium("air");  // PSD
  if (!medAir) Fatal("ConstructModule", "Medium air not found");

  TGeoMedium* medIron = gGeoManager->GetMedium("iron");  // Box
  if (!medIron) Fatal("ConstructModule", "Medium iron not found");

  TGeoMedium* medShieldPoly = gGeoManager->GetMedium("PsdPolyethylene");
  if (!medShieldPoly) Fatal("Main", "Medium PsdPolyethylene not found");

  TGeoVolume* shield = gGeoManager->MakeBox(
    name, medAir, 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * shieldLength);
  TGeoVolume* iron {nullptr};
  TGeoVolume* poly {nullptr};

  if (holesize > 0) {
    Int_t sx, sy;
    switch (hole_pos) {
      case 0:
        sx = 1;
        sy = 1;
        break;  // defines hole position in x-y plane
      case 1:
        sx = 1;
        sy = -1;
        break;
      case 2:
        sx = -1;
        sy = 1;
        break;
      case 3:
        sx = -1;
        sy = -1;
        break;
    }
    TGeoCombiTrans* rot45 =
      new TGeoCombiTrans(Form("rot45%s", suffix.Data()),
                         sx * sizeXY / 2.,
                         sy * sizeXY / 2.,
                         0.,
                         new TGeoRotation("rot", 45., 0., 0.));
    rot45->RegisterYourself();

    TGeoBBox* iron1 = new TGeoBBox(
      Form("iron1%s", suffix.Data()), 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * ironD);
    TGeoBBox* iron2 = new TGeoBBox(Form("iron2%s", suffix.Data()),
                                   0.5 * holesize,
                                   0.5 * holesize,
                                   0.5 * ironD);  //hole

    TGeoCompositeShape* ironShape = new TGeoCompositeShape(Form(
      "iron1%s-iron2%s:rot45%s", suffix.Data(), suffix.Data(), suffix.Data()));
    iron = new TGeoVolume(Form("iron%s", suffix.Data()), ironShape, medIron);

    TGeoBBox* poly1 = new TGeoBBox(
      Form("poly1%s", suffix.Data()), 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * polyD);
    TGeoBBox* poly2 = new TGeoBBox(Form("poly2%s", suffix.Data()),
                                   0.5 * holesize,
                                   0.5 * holesize,
                                   0.5 * polyD);  //hole

    TGeoCompositeShape* polyShape = new TGeoCompositeShape(Form(
      "poly1%s-poly2%s:rot45%s", suffix.Data(), suffix.Data(), suffix.Data()));
    poly =
      new TGeoVolume(Form("poly%s", suffix.Data()), polyShape, medShieldPoly);
  } else {
    iron = gGeoManager->MakeBox(
      "shield_iron", medIron, 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * ironD);
    poly = gGeoManager->MakeBox(
      "shield_poly", medShieldPoly, 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * polyD);
  }

  shield->AddNode(
    iron,
    0,
    new TGeoTranslation(0., 0., -shieldLength / 2. + psd2iron + ironD / 2.));
  shield->AddNode(
    poly,
    0,
    new TGeoTranslation(
      0., 0., -shieldLength / 2. + psd2iron + ironD + iron2poly + polyD / 2.));

  return shield;
}

/** ======================================================================= **/
TGeoVolume* ConstructModule(const char* name,
                            Double_t sizeXY,
                            Int_t nLayers,
                            fstream* info) {

  // The module consists of nLayers of scintillators covered with Tyvek.
  // Between each two scintillators, there is a lead layer (total nLayers -1).
  // At the top, there is a gap to stretch the light fibres. This is modelled
  // by a volume made of the same material as the scintillator (but inactive).
  // The arrangement is surrounded by iron. At the front and at the back,
  // there is a thick iron layer (the back one acting as first absorber)
  // for constructional reasons.

  cout << "===> Creating Module " << name << ", size " << sizeXY << " cm with "
       << nLayers << " layers...." << endl;

  const TString suffix =
    Form("_%d_%d", int(sizeXY), nLayers);  // unique suffix for volume names

  // -----   Constructional parameters   ----------------------------
  const Double_t leadD  = 1.60;  // Thickness of lead layer
  const Double_t scintD = 0.40;  // Thickness of scintillator layer
  const Double_t tyvekD =
    0.02;  // Thickness of Tyvek wrapper around scintillator
  const Double_t boxDx  = 0.15;  // Thickness of iron box lateral
  const Double_t boxDy  = 0.05;  // Thickness of iron box top/bottom
  const Double_t boxDz  = 2.00;  // Thickness of iron box front/back
  const Double_t chanDy = 0.20;  // Height of fibre channel
  const Double_t chanDistL =
    0.50;  // Distance of channel from left edge of lead
  const Double_t chanDistR =
    2.00;  // Distance of channel from right edge of lead
  // ------------------------------------------------------------------------


  // -----   Info to file   -------------------------------------------------
  if (info) {
    (*info) << "Parameters of module " << name << ": " << endl;
    (*info) << "Size: " << sizeXY << " cm x " << sizeXY << " cm" << endl;
    (*info) << "Number of layers: " << nLayers << endl;
    (*info) << "Thickness of lead layers: " << leadD << " cm" << endl;
    (*info) << "Thickness of scintillators: " << scintD << " cm" << endl;
    (*info) << "Thickness of Tyvek wrap: " << tyvekD << " cm" << endl;
    (*info) << "Thickness of iron box: (" << boxDx << " / " << boxDy << " / "
            << boxDz << ") cm" << endl;
    (*info) << "Height of fibre channel: " << chanDy << " cm" << endl;
    (*info) << "Distance of channel from edges: left " << chanDistL
            << " cm, right " << chanDistR << " cm" << endl;
  }
  // ------------------------------------------------------------------------


  // -----   Get required media   -------------------------------------------
  TGeoMedium* medAir = gGeoManager->GetMedium("air");  // PSD
  if (!medAir) Fatal("ConstructModule", "Medium air not found");
  TGeoMedium* medIron = gGeoManager->GetMedium("iron");  // Box
  if (!medIron) Fatal("ConstructModule", "Medium iron not found");
  TGeoMedium* medLead = gGeoManager->GetMedium("lead");  // Lead layers
  if (!medLead) Fatal("ConstructModule", "Medium lead not found");
  TGeoMedium* medTyvek = gGeoManager->GetMedium("PsdTyvek");  // Tyvek layers
  if (!medTyvek) Fatal("ConstructModule", "Medium PsdTyvek not found");
  TGeoMedium* medScint = gGeoManager->GetMedium("PsdScint");  // Scintillator
  if (!medScint) Fatal("ConstructModule", "Medium PsdScint not found");
  TGeoMedium* medFibre = gGeoManager->GetMedium("PsdFibre");  // Fibres
  if (!medFibre) Fatal("ConstructModule", "Medium PsdFibre not found");
  // ------------------------------------------------------------------------


  // -----   Create module volume   -----------------------------------------
  // Module length: nLayers of scintillators, nLayers - 1 of lead
  // plus the iron box front and back.
  // Material is iron.
  const Double_t moduleLength =
    2. * boxDz + nLayers * (scintD + 2. * tyvekD) + (nLayers - 1) * leadD;
  TGeoVolume* module = gGeoManager->MakeBox(
    name, medIron, 0.5 * sizeXY, 0.5 * sizeXY, 0.5 * moduleLength);
  module->SetLineColor(kRed);
  module->Print();
  cout << endl;
  // ------------------------------------------------------------------------


  // -----   Lead filler   --------------------------------------------------
  // The lead filler represents all lead absorber layers.
  // The Tyvek/scintillator layers and the fibre channel will be placed
  // inside.
  // Dimensions are half-lengths.
  const Double_t leadLx = 0.5 * sizeXY - boxDx;
  const Double_t leadLy = 0.5 * sizeXY - boxDy;
  const Double_t leadLz = 0.5 * moduleLength - boxDz;
  TGeoVolume* lead      = gGeoManager->MakeBox(
    Form("lead%s", suffix.Data()), medLead, leadLx, leadLy, leadLz);
  lead->SetLineColor(kBlue);
  // ------------------------------------------------------------------------


  // -----   Fibre channel   ------------------------------------------------
  // This volume represents the air gap at the top of the volume hosting the
  // light guide fibres. The material is the same as for the scintillators
  // (but inactive).
  // The (half-)width of the channel is defined by the distances from the
  // lead edges.
  const Double_t chanLx = leadLx - 0.5 * chanDistL - 0.5 * chanDistR;
  if (chanLx < 0.) {
    cout << "Error: lead volume is not large enough to host fibre channel!"
         << endl;
    cout << "Lead width: " << 2. * leadLx << ", distance from left edge "
         << chanDistL << ", distance from right edge " << chanDistR << endl;
    return 0;
  }
  const Double_t chanLy = 0.5 * chanDy;
  const Double_t chanLz = leadLz;
  TGeoVolume* channel   = gGeoManager->MakeBox(
    Form("channel%s", suffix.Data()), medFibre, chanLx, chanLy, chanLz);
  channel->SetLineColor(kMagenta);
  // ------------------------------------------------------------------------


  // ----   Positioning of the fibre channel   ------------------------------
  // The channel extends from chanDistL from the left edge of the lead filler
  // to chanDistR from its right edge (seen from the front). It is top-aligned
  // with the lead filler.
  const Double_t chanShiftX = 0.5 * (chanDistL - chanDistR);
  const Double_t chanShiftY = leadLy - 0.5 * chanDy;
  // ------------------------------------------------------------------------
  //     cout << " leadLx = " << leadLx << " leadLy = " << leadLy << endl;
  //     cout << " chanShiftX = " << chanShiftX << " chanShiftY = " << chanShiftY << endl;

  // -----   Tyvek layer   --------------------------------------------------
  // The scintillator will be placed inside this, leaving only the thin
  // Tyvek as a wrapper. Since these layers will be placed in the lead filler,
  // there has to be a cut-out for the fibre channel.
  const Double_t tyvekLz = 0.5 * scintD + tyvekD;  // half-length
  TGeoBBox* tyv1 =
    new TGeoBBox(Form("psdTyv1%s", suffix.Data()), leadLx, leadLy, tyvekLz);
  TGeoBBox* tyv2 =
    new TGeoBBox(Form("psdTyv2%s", suffix.Data()), chanLx, chanLy, tyvekLz);
  TGeoTranslation* trans1 = new TGeoTranslation(
    Form("tPsd1%s", suffix.Data()), chanShiftX, chanShiftY, 0.);

  trans1->RegisterYourself();
  TGeoCompositeShape* tyvekShape =
    new TGeoCompositeShape(Form("psdTyv1%s-psdTyv2%s:tPsd1%s",
                                suffix.Data(),
                                suffix.Data(),
                                suffix.Data()));
  TGeoVolume* tyvek =
    new TGeoVolume(Form("tyvek%s", suffix.Data()), tyvekShape, medTyvek);
  tyvek->SetLineColor(kGreen);
  // ------------------------------------------------------------------------


  // -----   Scintillator layer   -------------------------------------------
  // The scintillator layer is embedded (as daughter) into the Tyvek layer.
  // It is also a box minus the cut-out for the fibres. The cut-out is
  // slightly smaller than for the Tyvek volume.
  const Double_t scintLx = leadLx - tyvekD;
  const Double_t scintLy = leadLy - tyvekD;
  const Double_t scintLz = 0.5 * scintD;
  TGeoBBox* sci1 =
    new TGeoBBox(Form("sci1%s", suffix.Data()), scintLx, scintLy, scintLz);
  const Double_t cutLx = chanLx;
  const Double_t cutLy = chanLy - tyvekD;
  const Double_t cutLz = scintLz;
  TGeoBBox* sci2 =
    new TGeoBBox(Form("sci2%s", suffix.Data()), cutLx, cutLy, cutLz);
  const Double_t cutShiftX = chanShiftX;
  const Double_t cutShiftY = scintLy - cutLy;
  TGeoTranslation* trans2  = new TGeoTranslation(
    Form("tPsd2%s", suffix.Data()), cutShiftX, cutShiftY, 0.);
  trans2->RegisterYourself();
  TGeoCompositeShape* scintShape = new TGeoCompositeShape(
    Form("scintShape%s", suffix.Data()),
    Form("sci1%s-sci2%s:tPsd2%s", suffix.Data(), suffix.Data(), suffix.Data()));
  TGeoVolume* scint =
    new TGeoVolume(Form("scint%s", suffix.Data()), scintShape, medScint);
  scint->SetLineColor(kBlack);

  // ------------------------------------------------------------------------
  //     cout << " scintLx = " << scintLx << " scintLy = " << scintLy << endl;
  //     cout << " cutLx = " << cutLx << " cutLy = " << cutLy << endl;
  //     cout << " cutShiftX = " << cutShiftX << " cutShiftY = " << cutShiftY << endl;

  // -----   Place volumes   ------------------------------------------------

  // ---> Scintillator into Tyvek
  tyvek->AddNode(scint, 0);

  // ---> Fibre channel into lead filler
  lead->AddNode(channel, 0, trans1);

  // --->  Tyvek layers into lead
  Double_t zFirst {0.};
  Double_t zLast {0.};
  for (Int_t iLayer = 0; iLayer < nLayers; iLayer++) {
    const Double_t zPos =
      -1. * leadLz + iLayer * leadD + (2. * iLayer + 1.) * tyvekLz;
    if (iLayer == 0) zFirst = zPos;
    if (iLayer == nLayers - 1) zLast = zPos;
    lead->AddNode(tyvek, iLayer, new TGeoTranslation(0., 0., zPos));
  }
  cout << module->GetName() << ": Positioned " << nLayers
       << " Tyvek layers; first at z = " << zFirst << ", last at z = " << zLast
       << endl;

  // ---> Lead into module
  module->AddNode(lead, 0);
  // ------------------------------------------------------------------------

  return module;
}


TGeoVolume* ConstructModuleWithHole(const char* name,
                                    Double_t sizeXY,
                                    Int_t nLayers,
                                    fstream* info,
                                    float holesize,
                                    Int_t hole_pos) {

  // The module consists of nLayers of scintillators covered with Tyvek.
  // Between each two scintillators, there is a lead layer (total nLayers -1).
  // At the top, there is a gap to stretch the light fibres. This is modelled
  // by a volume made of the same material as the scintillator (but inactive).
  // The arrangement is surrounded by iron. At the front and at the back,
  // there is a thick iron layer (the back one acting as first absorber)
  // for constructional reasons.


  if (holesize == 0.) return ConstructModule(name, sizeXY, nLayers, info);

  const TString suffix =
    Form("_%d_%d_%d", int(sizeXY), int(holesize), int(hole_pos));

  cout << "===> Creating Module " << name << ", size " << sizeXY << " cm with "
       << nLayers << " layers...." << endl;

  // -----   Constructional parameters   ----------------------------
  const Double_t leadD  = 1.60;  // Thickness of lead layer
  const Double_t scintD = 0.40;  // Thickness of scintillator layer
  const Double_t tyvekD =
    0.02;  // Thickness of Tyvek wrapper around scintillator

  const Double_t boxDx = 0.15;  // Thickness of iron box lateral
  const Double_t boxDy = 0.05;  // Thickness of iron box top/bottom
  const Double_t boxDz = 2.00;  // Thickness of iron box front/back

  const Double_t chanDy = 0.20;  // Height of fibre channel
  const Double_t chanDistL =
    0.50;  // Distance of channel from left edge of lead
  const Double_t chanDistR =
    2.00;  // Distance of channel from right edge of lead

  const Double_t moduleLength =
    2. * boxDz + nLayers * (scintD + 2. * tyvekD) + (nLayers - 1) * leadD;

  // Dimensions are half-lengths.
  const Double_t leadLx  = 0.5 * sizeXY - boxDx;
  const Double_t leadLy  = 0.5 * sizeXY - boxDy;
  const Double_t leadLz  = 0.5 * moduleLength - boxDz;
  const Double_t tyvekLz = 0.5 * scintD + tyvekD;  // half-length

  const Double_t scintLx = leadLx - tyvekD;  // Scintillator size x
  const Double_t scintLy = leadLy - tyvekD;  // Scintillator size y
  const Double_t scintLz = 0.5 * scintD;     // Scintillator size z


  // ------------------------------------------------------------------------
  Int_t sx, sy;
  switch (hole_pos) {
    case 0:
      sx = 1;
      sy = 1;
      break;  // defines hole position in x-y plane
    case 1:
      sx = 1;
      sy = -1;
      break;
    case 2:
      sx = -1;
      sy = 1;
      break;
    case 3:
      sx = -1;
      sy = -1;
      break;
  }
  // -----   Info to file   -------------------------------------------------
  if (info) {
    (*info) << "Parameters of module " << name << ": " << endl;
    (*info) << "Size: " << sizeXY << " cm x " << sizeXY << " cm" << endl;
    (*info) << "Number of layers: " << nLayers << endl;
    (*info) << "Thickness of lead layers: " << leadD << " cm" << endl;
    (*info) << "Thickness of scintillators: " << scintD << " cm" << endl;
    (*info) << "Thickness of Tyvek wrap: " << tyvekD << " cm" << endl;
    (*info) << "Thickness of iron box: (" << boxDx << " / " << boxDy << " / "
            << boxDz << ") cm" << endl;
    (*info) << "Height of fibre channel: " << chanDy << " cm" << endl;
    (*info) << "Distance of channel from edges: left " << chanDistL
            << " cm, right " << chanDistR << " cm" << endl;
  }
  // ------------------------------------------------------------------------

  // -----   Get required media   -------------------------------------------
  TGeoMedium* medAir = gGeoManager->GetMedium("air");  // PSD
  if (!medAir) Fatal("ConstructModule", "Medium air not found");
  TGeoMedium* medIron = gGeoManager->GetMedium("iron");  // Box
  if (!medIron) Fatal("ConstructModule", "Medium iron not found");
  TGeoMedium* medLead = gGeoManager->GetMedium("lead");  // Lead layers
  if (!medLead) Fatal("ConstructModule", "Medium lead not found");
  TGeoMedium* medTyvek = gGeoManager->GetMedium("PsdTyvek");  // Tyvek layers
  if (!medTyvek) Fatal("ConstructModule", "Medium PsdTyvek not found");
  TGeoMedium* medScint = gGeoManager->GetMedium("PsdScint");  // Scintillator
  if (!medScint) Fatal("ConstructModule", "Medium PsdScint not found");
  TGeoMedium* medFibre = gGeoManager->GetMedium("PsdFibre");  // Fibres
  if (!medFibre) Fatal("ConstructModule", "Medium PsdFibre not found");
  // ------------------------------------------------------------------------

  TGeoCombiTrans* rot45 =
    new TGeoCombiTrans(Form("rot45%s", suffix.Data()),
                       sx * sizeXY / 2.,
                       sy * sizeXY / 2.,
                       0.,
                       new TGeoRotation("rot", 45., 0., 0.));
  rot45->RegisterYourself();

  // -----   Create module volume   -----------------------------------------
  // Module length: nLayers of scintillators, nLayers - 1 of lead
  // plus the iron box front and back.
  // Material is iron.
  //     TGeoVolume* module = gGeoManager->MakeBox(name, medIron, 0.5 * sizeXY,
  //                                               0.5 * sizeXY, 0.5 * moduleLength);

  TGeoBBox* module1 = new TGeoBBox(Form("module1%s", suffix.Data()),
                                   0.5 * sizeXY,
                                   0.5 * sizeXY,
                                   0.5 * moduleLength);
  TGeoBBox* module2 = new TGeoBBox(Form("module2%s", suffix.Data()),
                                   0.5 * holesize,
                                   0.5 * holesize,
                                   0.5 * moduleLength);  //hole
  TGeoCompositeShape* moduleShape =
    new TGeoCompositeShape(Form("module1%s-module2%s:rot45%s",
                                suffix.Data(),
                                suffix.Data(),
                                suffix.Data()));
  TGeoVolume* module = new TGeoVolume(name, moduleShape, medIron);

  module->SetLineColor(kRed);
  module->Print();
  cout << endl;
  // ------------------------------------------------------------------------


  // -----   Lead filler   --------------------------------------------------
  // The lead filler represents all lead absorber layers.
  // The Tyvek/scintillator layers and the fibre channel will be placed
  // inside.
  //     TGeoVolume* lead = gGeoManager->MakeBox(Form("lead%s", suffix.Data()), medLead, leadLx, leadLy, leadLz);

  TGeoBBox* lead1 =
    new TGeoBBox(Form("lead1%s", suffix.Data()), leadLx, leadLy, leadLz);
  TGeoBBox* lead2               = new TGeoBBox(Form("lead2%s", suffix.Data()),
                                 0.5 * holesize,
                                 0.5 * holesize,
                                 leadLz);  //hole
  TGeoCompositeShape* leadShape = new TGeoCompositeShape(Form(
    "lead1%s-lead2%s:rot45%s", suffix.Data(), suffix.Data(), suffix.Data()));
  TGeoVolume* lead =
    new TGeoVolume(Form("lead%s", suffix.Data()), leadShape, medLead);

  lead->SetLineColor(kBlue);
  // ------------------------------------------------------------------------

  // -----   Tyvek layer   --------------------------------------------------
  // The scintillator will be placed inside this, leaving only the thin
  // Tyvek as a wrapper. Since these layers will be placed in the lead filler,
  // there has to be a cut-out for the fibre channel.

  TGeoBBox* tyv1 =
    new TGeoBBox(Form("psdTyv1%s", suffix.Data()), leadLx, leadLy, tyvekLz);
  TGeoBBox* tyv2 = new TGeoBBox(Form("psdTyv2%s", suffix.Data()),
                                holesize / 2.,
                                holesize / 2.,
                                tyvekLz);  //hole

  TGeoCompositeShape* tyvekShape =
    new TGeoCompositeShape(Form("psdTyv1%s-psdTyv2%s:rot45%s",
                                suffix.Data(),
                                suffix.Data(),
                                suffix.Data()));
  TGeoVolume* tyvek =
    new TGeoVolume(Form("tyvek%s", suffix.Data()), tyvekShape, medTyvek);
  tyvek->SetLineColor(kGreen);
  // ------------------------------------------------------------------------


  // -----   Scintillator layer   -------------------------------------------
  // The scintillator layer is embedded (as daughter) into the Tyvek layer.
  // It is also a box minus the cut-out for the fibres. The cut-out is
  // slightly smaller than for the Tyvek volume.
  TGeoBBox* sci1 =
    new TGeoBBox(Form("sci1%s", suffix.Data()), scintLx, scintLy, scintLz);
  TGeoBBox* sci2                 = new TGeoBBox(Form("sci2%s", suffix.Data()),
                                holesize / 2.,
                                holesize / 2.,
                                scintLz);  //hole
  TGeoCompositeShape* scintShape = new TGeoCompositeShape(
    Form("scintShape%s", suffix.Data()),
    Form("sci1%s-sci2%s:rot45%s", suffix.Data(), suffix.Data(), suffix.Data()));
  TGeoVolume* scint =
    new TGeoVolume(Form("scint%s", suffix.Data()), scintShape, medScint);
  scint->SetLineColor(kBlack);

  // -----   Place volumes   ------------------------------------------------

  // ---> Scintillator into Tyvek
  tyvek->AddNode(scint, 0);

  // ---> Fibre channel into lead filler
  //     lead->AddNode(channel, 0, trans1);   //Needs addidional size check

  // --->  Tyvek layers into lead
  Double_t zFirst {0.};
  Double_t zLast {0.};
  for (Int_t iLayer = 0; iLayer < nLayers; iLayer++) {
    const Double_t zPos =
      -1. * leadLz + iLayer * leadD + (2. * iLayer + 1.) * tyvekLz;
    if (iLayer == 0) zFirst = zPos;
    if (iLayer == nLayers - 1) zLast = zPos;
    lead->AddNode(tyvek, iLayer, new TGeoTranslation(0., 0., zPos));
  }
  cout << module->GetName() << ": Positioned " << nLayers
       << " Tyvek layers; first at z = " << zFirst << ", last at z = " << zLast
       << endl;

  // ---> Lead into module
  module->AddNode(lead, 0);
  // ------------------------------------------------------------------------

  return module;
}