Skip to content
Snippets Groups Projects
Forked from Chad Lantz / zdcLG
This fork has diverged from the upstream repository.
DetectorConstruction.cc 19.45 KiB

// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
/// \file /src/DetectorConstruction.cc
/// \brief Implementation of the DetectorConstruction class
//
//

// USER //
#include "DetectorConstruction.hh"
#include "DetectorMessenger.hh"
#include "Materials.hh"
#include "PMTSD.hh"

#include "CADMesh.hh"

#include <math.h>

// GEANT4 //
#include "G4Element.hh"
#include "G4SDManager.hh"
#include "G4RunManager.hh"
#include "G4GeometryManager.hh"
#include "G4PhysicalVolumeStore.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4SolidStore.hh"
#include "G4LogicalBorderSurface.hh"
#include "G4LogicalSkinSurface.hh"
#include "G4OpticalSurface.hh"
#include "G4ThreeVector.hh"
#include "G4PVPlacement.hh"
#include "G4SystemOfUnits.hh"
#include "G4Tubs.hh"
#include "G4Trd.hh"
#include "G4SubtractionSolid.hh"
#include "G4NistManager.hh"
#include "G4VisAttributes.hh"
#include "G4VisExtent.hh"
#include "G4Colour.hh"
#include "G4UImanager.hh"

/*
 *
 */
DetectorConstruction::DetectorConstruction()
  : G4VUserDetectorConstruction(),
    m_worldDim( new G4ThreeVector(1.0*m, 1.0*m, 1.0*m) ),
    m_LGpos( new G4ThreeVector() ),
    m_pmtPos( new G4ThreeVector(28.5*mm, 35.0*mm, -7.5*mm) ),
    m_rotation( new G4RotationMatrix() ),
    m_pmtDia(10.5*mm),
    m_PMTthickness(0.001*mm),
    m_ConstructionHasBeenDone(false),
    m_UsingCADmodel(true),
    m_logicWorld(0),
    m_physLG(0),
    m_physPMT(0),
    m_DetectorMessenger(nullptr)
{
  materials = Materials::getInstance();
  materials->UseOpticalMaterials(true);
  materials->DefineOpticalProperties();

  m_DetectorMessenger = new DetectorMessenger(this);
  m_runMan = G4RunManager::GetRunManager();

}


/*
 *
 */
DetectorConstruction::~DetectorConstruction(){
  delete m_DetectorMessenger;
}

/*
 * Execute geometry.mac here and construct the world according
 * to what it contains
 */
G4VPhysicalVolume* DetectorConstruction::Construct(){
  if ( m_ConstructionHasBeenDone ) {
    G4GeometryManager::GetInstance()->OpenGeometry();
    G4PhysicalVolumeStore::GetInstance()->Clean();
    G4LogicalVolumeStore::GetInstance()->Clean();
    G4SolidStore::GetInstance()->Clean();
    G4LogicalSkinSurface::CleanSurfaceTable();
    G4LogicalBorderSurface::CleanSurfaceTable();
  } else {
    G4UImanager* UImanager = G4UImanager::GetUIpointer();
    UImanager->ApplyCommand("/control/execute geometry.mac");
    BuildPMT();
  }

  if(!m_logicWorld) BuildWorld();
  if(!m_physLG) BuildDefaultLG();
  PlaceGeometry();

  m_ConstructionHasBeenDone = true;

  return m_physWorld;

}

/*
 *
 */
void DetectorConstruction::BuildWorld(){
  bool checkOverlaps = false;

  //----------------- Define the world volume -----------------//
      m_solidWorld =
	new G4Box("World",       //name
		  m_worldDim->x(),  //sizeX
		  m_worldDim->y(),  //sizeY
		  m_worldDim->z()); //sizeZ

            m_logicWorld =
	      new G4LogicalVolume(m_solidWorld, //solid
				  materials->Air,          //material
				  "World");     //name

	            m_physWorld =
		      new G4PVPlacement(0,                  //no rotation
					G4ThreeVector(),    //at (0,0,0)
					m_logicWorld,       //logical volume
					"World",            //name
					0,                  //mother  volume
					false,              //no boolean operation
					0,                  //copy number
					checkOverlaps);     //overlaps checking

		   // G4VisAttributes* boxVisAtt_world= new G4VisAttributes();     ***UNCOMMENT FOR VISIBLE WORLD VOLUME***
		   G4VisAttributes* boxVisAtt_world= new G4VisAttributes(G4VisAttributes::Invisible);
		   m_logicWorld ->SetVisAttributes(boxVisAtt_world);
}

/*
 * Make the default light guide
 */
void DetectorConstruction::BuildDefaultLG( ){

  // Build the light guide here up until the logical volume
  // Maybe build the aluminum portion into a single CAD solid and add
  // fibers and air here

  UseCADModel("../models/LED-GMS-light-guide.stl");

  bool CHECK_OVERLAPS = false;

  //Change these variables to what they really should be
  double fiberLength = 270*mm;
  double core_diam = 0.600*mm;
  double cladding_diam = 0.660*mm;
  double buffer_diam = 0.710*mm;

  //Core
  m_fiberCore = new G4Tubs("fiber_core", // name
			   0.0*mm,       // inner radius
			   core_diam/2.0,// outter radius
			   fiberLength,  // length
			   0.0*deg,      // sweep starting angle
			   360.0*deg);   // sweep stopping angle

  m_logicCore = new G4LogicalVolume( m_fiberCore,        // solid to be created from
				     materials->pQuartz, // material
				     "fiber_logicCore"); // name

  //Cladding
  m_fiberCladding = new G4Tubs("fiber_cladding",  // name
			       core_diam/2.0,     // inner radius
			       cladding_diam/2.0, // outter radius
			       fiberLength,       // length
			       0.0*deg,           // sweep starting angle
			       360.0*deg);        // sweep stopping angle

  m_logicCladding = new G4LogicalVolume( m_fiberCladding,    // solid to be created from
					 materials->pQuartz, // material
					 "fiber_logicCladding"); // name

  //Buffer
  m_fiberBuffer = new G4Tubs("fiber_buffer",   // name
			     cladding_diam/2.0,// inner radius
			     buffer_diam/2.0,  // outter radius
			     fiberLength,      // length
			     0.0*deg,          // sweep starting angle
			     360.0*deg);       // sweep stopping angle
  m_logicBuffer = new G4LogicalVolume( m_fiberBuffer,      // solid to be created from
				       materials->pQuartz, // material
				       "fiber_logicBuffer"); // name

  // Place the fibers
  G4RotationMatrix* fiberRotation = new G4RotationMatrix();
  fiberRotation->rotateX(90.*deg);
  char name[32];
  for(int i = 0; i < 32; i++){

    // calculate cooridnates for center of each fiber (use CAD model for placement):
    double x = 8.5 + 1.20*i;  // x-offset +  fiber spacing
    double y = 345;
    double z = -6.00;
    //Core
    sprintf(name,"fiberCore_%d", i);
    m_physCore.push_back(
			 new G4PVPlacement(fiberRotation,                  //Rotation
					   G4ThreeVector( x , y , z ), //Position
					   m_logicCore,                    //Logical volume this is built from
					   name,                           //Name
					   m_logicWorld,                   //Mother volume
					   false,                          //If there are boolean operations
					   i,                              //Copy number
					   CHECK_OVERLAPS) );              //Check for overlaps with other geometry


    //Cladding
    sprintf(name,"fiberCladding_%d", i);
    m_physCladding.push_back(
			     new G4PVPlacement(fiberRotation,                  //Rotation
					       G4ThreeVector( x , y , z ), //Position
					       m_logicCladding,                //Logical volume this is built from
					       name,                           //Name
					       m_logicWorld,                   //Mother volume
					       false,                          //If there are boolean operations
					       i,                              //Copy number
					       CHECK_OVERLAPS) );              //Check for overlaps with other geometry


    //Core
    sprintf(name,"fiberBuffer_%d", i);
    m_physBuffer.push_back(
			   new G4PVPlacement(fiberRotation,                  //Rotation
					     G4ThreeVector( x , y, z ), //Position
					     m_logicBuffer,                  //Logical volume this is built from
					     name,                           //Name
					     m_logicWorld,                   //Mother volume
					     false,                          //If there are boolean operations
					     i,                              //Copy number
					     CHECK_OVERLAPS) );              //Check for overlaps with other geometry

  }
}


/*
 * Make the PMT logical volume and Sensitvie Detector
 */
void DetectorConstruction::BuildPMT(){

  G4SDManager* SDman = G4SDManager::GetSDMpointer();
  m_SDnames.push_back("MyPMT");
  PMTSD* PMT = new PMTSD(m_SDnames.back(), false); // isIntermediate set to false. This is the final detector
  SDman->AddNewDetector( PMT );

      m_solidPMT =
	 new G4Tubs("PMT",         //name
		   0.0*mm,         //Inner radius
		   m_pmtDia/2.0,   //Outter radius
		   m_PMTthickness, //Height
		   0.0*deg,        //Rotation start
		   360.0*deg);     //Sweep


            m_logicPMT =
	      new G4LogicalVolume(m_solidPMT,       //solid
				  materials->Air,   //material
				  "PMT");           //name

	    G4VisAttributes* VisAtt_PMT = new G4VisAttributes(G4Colour(1.0,1.0,0.6,0.7));
	    m_logicPMT->SetVisAttributes(VisAtt_PMT);
	    m_logicPMT->SetSensitiveDetector( PMT );


      /***********************************
       * Intermediate Sensitive Detector 1
       **********************************/
      m_SDnames.push_back("IntSD1");
      PMT = new PMTSD(m_SDnames.back(), true); // isIntermediate set to true
      SDman->AddNewDetector( PMT );
      m_solidIntSD.push_back( new G4Box("soldIntSD1",20.00*mm, 0.001*mm, 2.00*mm) );
      m_logicIntSD.push_back( new G4LogicalVolume(m_solidIntSD.back(), materials->Air, "logicIntSD1") );
      m_logicIntSD.back()->SetVisAttributes(VisAtt_PMT);
      m_logicIntSD.back()->SetSensitiveDetector( PMT );

      /***********************************
       * Intermediate Sensitive Detector 2
       **********************************/
      m_SDnames.push_back("IntSD2");
      PMT = new PMTSD(m_SDnames.back(), true); // isIntermediate set to true
      SDman->AddNewDetector( PMT );
      m_solidIntSD.push_back( new G4Box("soldIntSD2",50.00*mm, 0.001*mm, 1.00*mm) );
      m_logicIntSD.push_back( new G4LogicalVolume(m_solidIntSD.back(), materials->Air, "logicIntSD2") );
      m_logicIntSD.back()->SetVisAttributes(VisAtt_PMT);
      m_logicIntSD.back()->SetSensitiveDetector( PMT );

}

/*
 * Place the geometry. This is done in it's own step in case
 * the user has specified a CAD model to be used
 */
void DetectorConstruction::PlaceGeometry(){

  //----------------- Place the LightGuide -----------------//

  m_physLG = new G4PVPlacement(m_rotation,
			       *m_LGpos,
			       m_logicLG,
			       "LightGuide",
			       m_logicWorld,
			       false,
			       0);

  //----------------- Place the PMT -----------------//

  m_physPMT = new G4PVPlacement(new G4RotationMatrix(),
				*m_pmtPos,
				m_logicPMT,
				"PMT",
				m_logicWorld,
				false,
				0);


  //----------------- Place Intermediate Detector 1 -----------------//

  m_physIntSD.push_back(
    new G4PVPlacement(new G4RotationMatrix(),
				              G4ThreeVector( 28.50*mm, 615.00*mm, -5.50*mm),
				              m_logicIntSD[0],
				              "IntSD1",
				              m_logicWorld,
				              false,
				              0) );
  //----------------- Place Intermediate Detector 2 -----------------//

  m_physIntSD.push_back(
    new G4PVPlacement(new G4RotationMatrix(),
                                              G4ThreeVector( 28.50*mm, 57.50*mm, -6.00*mm),
                                              m_logicIntSD[0],
                                              "IntSD2",
                                              m_logicWorld,
                                              false,
                                              0) );


  //----------------- Define Optical Borders -----------------//

  // One of these is correct. The order of the logical volumes is different
  // between the two. This is specific to the direction of light i.e. from
  // volume a to volume b or vice versa

  // m_Surface = new G4LogicalBorderSurface("AlSurface",
  //                              m_physLightGuide,
  //                              m_physWorld,
  //                              materials->AlSurface ) );
  //
  m_Surface = new G4LogicalBorderSurface("AlSurface",
					 m_physWorld,
					 m_physLG,
					 materials->AlSurface );

}

/*
 * Set the dimensions of the world volume.
 * If the volume exists modify it, otherwise build it.
 */
void DetectorConstruction::SetWorldVolume(G4ThreeVector arg){
  if(m_ConstructionHasBeenDone){
    m_solidWorld->SetXHalfLength( arg.x() );
    m_solidWorld->SetYHalfLength( arg.y() );
    m_solidWorld->SetZHalfLength( arg.z() );
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  } else {
    delete m_worldDim;
    m_worldDim = new G4ThreeVector(arg);
    BuildWorld();
  }
}

/*
 * If the world already exists, rotate the light guide
 * otherwise set the rotation for the light guide that will be built
 */
void DetectorConstruction::SetRotation(G4ThreeVector arg){
  if(m_rotation) delete m_rotation;
  m_rotation = new G4RotationMatrix();

  m_rotation->rotateX(arg.x());
  m_rotation->rotateY(arg.y());
  m_rotation->rotateZ(arg.z());

  if(m_ConstructionHasBeenDone){
    m_physLG->SetRotation(m_rotation);
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  }
}

/*
 * If the world already exists, move the light guide. If there is more
 * than one light guide, set the position and rebuild.
 * otherwise set the location for the light guide that will be built
 */
void DetectorConstruction::SetTranslation(G4ThreeVector arg){
  if(m_ConstructionHasBeenDone){
    m_physLG->SetTranslation(arg);
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  } else {
    m_LGpos = new G4ThreeVector(arg);
  }
}


/*
 * If the world already exists, move the PMT. If there is more
 * than one PMT, set the position and rebuild.
 * otherwise set the location for the PMT that will be built
 */
void DetectorConstruction::SetPMTTranslation(G4ThreeVector arg){
  if(m_ConstructionHasBeenDone){
    m_physPMT->SetTranslation(arg);
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  } else {
    m_pmtPos = new G4ThreeVector(arg);
  }
}

/*
 * If the world already exists, modify the PMT
 * otherwise set the diameter for PMT that will be built
 */
void DetectorConstruction::SetPMTDiameter(G4double arg){
  if(m_ConstructionHasBeenDone){
    m_solidPMT->SetOuterRadius(arg/2.0);
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  } else {
    m_pmtDia = arg;
  }
}


/*
 * Builds a light guide logical volume from CAD model file
 */
void DetectorConstruction::UseCADModel(G4String fileName){
  //Delete anything we are going to redefine
  if(m_ConstructionHasBeenDone){
    delete m_logicLG;
    delete m_physLG;
    delete m_physPMT;
    delete m_Surface;
  }

  float xSize=0.0, ySize=0.0, zSize=0.0;

  G4String fileType = fileName.substr( fileName.last('.') + 1, fileName.size() - fileName.last('.'));

  if(fileType == "stl"){
    auto mesh = CADMesh::TessellatedMesh::FromSTL((char*) fileName.c_str());
    // auto mesh = CADMesh::TessellatedMesh::From((char*) fileName.c_str());
    mesh->SetScale(mm);
    mesh->SetOffset( G4ThreeVector(0, 0, 0) );
    mesh->SetReverse(false);

            m_logicLG =
	      new G4LogicalVolume(mesh->GetSolid(), //solid
				  materials->Al,           //material
				  "LightGuide");           //name
  }else{
    G4cout << "File type not supported" << G4endl;
  }


  if(m_logicLG !=0 ){

    //Print the extent to the console to help the user position the light guide
    char message[64];
    G4VisExtent extent = m_logicLG->GetSolid()->GetExtent();
    xSize = extent.GetXmax() - extent.GetXmin();
    ySize = extent.GetYmax() - extent.GetYmin();
    zSize = extent.GetZmax() - extent.GetZmin();
    G4cout << "===== Light guide extent =====" << G4endl;
    sprintf(message,"Xmin = %04.2f, Xmax = %04.2f: Width  = %04.2f", extent.GetXmin(), extent.GetXmax(), xSize );
    G4cout << message << G4endl;
    sprintf(message,"Ymin = %04.2f, Ymax = %04.2f: Height = %04.2f", extent.GetYmin(), extent.GetYmax(), ySize );
    G4cout << message << G4endl;
    sprintf(message,"Zmin = %04.2f, Zmax = %04.2f: Depth  = %04.2f", extent.GetZmin(), extent.GetZmax(), zSize );
    G4cout << message << G4endl;

  }

  if(m_pmtPos == 0) m_pmtPos = new G4ThreeVector( );

  if(m_ConstructionHasBeenDone){
    PlaceGeometry();
    m_runMan->GeometryHasBeenModified();
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/rebuild");
  }
  G4cout << "Using " << fileName << " for CAD model" << G4endl;
}


/*
 * Set the surface model for the light guide
 */
void DetectorConstruction::SetSurfaceModel(const G4OpticalSurfaceModel model){
  materials->AlSurface->SetModel(model);
  if(m_ConstructionHasBeenDone){
    m_runMan->GeometryHasBeenModified();
  }
}

/*
 *
 */
void DetectorConstruction::SetSurfaceFinish(const G4OpticalSurfaceFinish finish){
  materials->AlSurface->SetFinish(finish);
  if(m_ConstructionHasBeenDone){
    m_runMan->GeometryHasBeenModified();
  }
}

/*
 *
 */
void DetectorConstruction::SetSurfaceType(const G4SurfaceType type){
  materials->AlSurface->SetType(type);
  if(m_ConstructionHasBeenDone){
    m_runMan->GeometryHasBeenModified();
  }
}
/*
 *
 */
void DetectorConstruction::SetSurfaceSigmaAlpha(G4double v){
  materials->AlSurface->SetSigmaAlpha(v);

  if(m_ConstructionHasBeenDone){
    G4RunManager::GetRunManager()->GeometryHasBeenModified();
  }

  G4cout << "Surface sigma alpha set to: " << materials->AlSurface->GetSigmaAlpha() << G4endl;
}

/*
 * Set the material property table of the surface
 * of the light guide
 */
void DetectorConstruction::AddSurfaceMPV(const char* c, G4MaterialPropertyVector* mpv){
  mpv->SetSpline(true);
  materials->GetMPTArray().at(1)->AddProperty(c, mpv);
  materials->AlSurface->SetMaterialPropertiesTable(materials->GetMPTArray().at(1));
  G4cout << "The MPT for the surface is now: " << G4endl;
  materials->GetMPTArray().at(1)->DumpTable();
  G4cout << "............." << G4endl;
}