Detector.jl

```julia #————————————————————————————————–

#————————————————————————————————– mutable struct ScintDetector <: G4JLDetector # main input parameters const worldSize::Float64 const doTeflon::Bool const doAluminum::Bool const cryssize::Float64 const siside::Float64 const sithick::Float64 const tefthick::Float64 const althick::Float64 const airgap::Float64 const checkOverlaps::Bool # calculated variables vacMat::CxxPtr{G4Material} cebr3Mat::CxxPtr{G4Material} qzMat::CxxPtr{G4Material} alMat::CxxPtr{G4Material} tefMat::CxxPtr{G4Material} siMat::CxxPtr{G4Material} beMat::CxxPtr{G4Material} # constructor with defaults values for parameters function ScintDetector(; worldSize = 5cm, doTeflon = true, doAluminum = true, cryssize = 2cm, siside = 1cm, sithick = 2mm, tefthick = 1mm, althick = 1mm, airgap = 10mm, checkOverlaps = true ) self = new(worldSize, doTeflon, doAluminum, cryssize, siside, sithick, tefthick, althick, airgap, checkOverlaps) return self end end

function defineMaterials!(det::ScintDetector) # to avoid warnings of redefinitions G4Material!GetMaterial("vacumm", false) != C_NULL && return

nist = G4NistManager!Instance()

#---vacuum------------------------------------------------------------------------------------- 
vacMat = G4Material("vacumm", z=1., a=1.01g/mole, density=universe_mean_density, state=kStateGas, 
                    temperature=VACUUM_TEMPERATURE, pressure=VACUUM_PRESSURE)
vacPt = G4MaterialPropertiesTable()
AddProperty(vacPt, "RINDEX", CEBR3_REFR_IDX_ENERGIES, StdVector(fill(1., length(CEBR3_REFR_IDX_ENERGIES))), useSpline)
SetMaterialPropertiesTable(vacMat, move!(vacPt))
det.vacMat = CxxPtr(vacMat)

#---cebr3--------------------------------------------------------------------------------------
ce = FindOrBuildElement(nist, "Ce")
br = FindOrBuildElement(nist, "Br")
cebr3 = G4Material("cebr3", density=CEBR3_DENSITY, ncomponents=2, state=kStateSolid, temperature=SATELLITE_TEMP)
AddElement(cebr3, br, BR_MASS_FRAC)
AddElement(cebr3, ce, CE_MASS_FRAC)

#  optical parameters
opp = G4OpticalParameters!Instance()
SetScintFiniteRiseTime(opp, false)

scintPt = G4MaterialPropertiesTable()

AddConstProperty(scintPt, "SCINTILLATIONYIELD", CEBR3_SCINT_YIELD)
AddProperty(scintPt, "RINDEX", CEBR3_REFR_IDX_ENERGIES, CEBR3_REFR_IDXS, useSpline)
AddProperty(scintPt, "SCINTILLATIONCOMPONENT1", CEBR3_SCINT_OPTICAL_ENERGIES, CEBR3_SCINT_OPTICAL_INTENSITIES, useSpline)
AddConstProperty(scintPt, "SCINTILLATIONTIMECONSTANT1", CEBR3_DECAY_TIME_CONSTANT)

# # of photons emitted = RESOLUTION_SCALE * sqrt(mean # of photons)
AddConstProperty(scintPt, "RESOLUTIONSCALE", CEBR3_SCINT_RESLN_SCALE)
AddProperty(scintPt, "ABSLENGTH", CEBR3_ABS_LEN_ENERGIES, CEBR3_ABS_LEN, useSpline)
# skip optical Rayleigh scattering (not important)
# skip Mie scattering (doesn't apply)
SetMaterialPropertiesTable(cebr3, move!(scintPt))
det.cebr3Mat = CxxPtr(cebr3)

#---quartz-------------------------------------------------------------------------------------
qz = FindOrBuildMaterial(nist, "G4_SILICON_DIOXIDE")
qzPt = G4MaterialPropertiesTable()
AddProperty(qzPt, "RINDEX", QZ_REFR_IDX_ENERGIES, QZ_REFR_IDXS )
# do not add real/imag refractive indices to materials. they areo nly for surface indices.
# qzPt->AddProperty(kREFR_IDX_REAL, QZ_REFR_IDX_ENERGIES, QZ_REFR_IDXS);
# qzPt->AddProperty(kREFR_IDX_IMAG, QZ_REFR_IDX_ENERGIES, std::vector<G4double>(QZ_REFR_IDXS.size(), 0.0));
SetMaterialPropertiesTable(qz, move!(qzPt))
det.qzMat = qz

#---teflon--------------------------------------------------------------------------------------
teflon = FindOrBuildMaterial(nist, "G4_TEFLON")
tefPt = G4MaterialPropertiesTable()
AddProperty(tefPt, "RINDEX", TEFLON_REFR_IDX_ENERGIES, TEFLON_REFR_IDXS, useSpline)
AddProperty(tefPt, "REFLECTIVITY", TEFLON_REFR_IDX_ENERGIES, TEFLON_REFLECTIVITY, useSpline)
SetMaterialPropertiesTable(teflon, move!(tefPt))
det.tefMat = CxxPtr(teflon)

#---Silicon------------------------------------------------------------------------------------
si = FindOrBuildMaterial(nist, "G4_Si")
simPt = G4MaterialPropertiesTable()
# set to one and apply detection in post-processing (?)
AddProperty(simPt, "EFFICIENCY", SI_DET_EFF_ENERGIES, SI_DET_EFF, useSpline)
refl = StdVector(fill(0., length(SI_BROADCOM_NUMBERS)))
AddProperty(simPt, "REFLECTIVITY", SI_DET_EFF_ENERGIES, refl, useSpline)
AddProperty(simPt, "TRANSMITTANCE", SI_DET_EFF_ENERGIES, SI_TRANSMITTANCE, useSpline)
AddProperty(simPt, "RINDEX", SI_REFR_IDX_ENERGY, SI_REFR_IDX_REAL, useSpline)
AddProperty(simPt, "REALRINDEX", SI_REFR_IDX_ENERGY, SI_REFR_IDX_REAL, useSpline)
AddProperty(simPt, "IMAGINARYRINDEX", SI_REFR_IDX_ENERGY, SI_REFR_IDX_IMAG, useSpline)
SetMaterialPropertiesTable(si, move!(simPt))
det.siMat = si

#---aluminum-----------------------------------------------------------------------------------
alElt = FindOrBuildElement(nist, "Al")
alElt == C_NULL &&  error("Nist manager gave nullptr for aluminum")

al = G4Material("aluminum", density=2.712g/cm3, ncomponents=1, state=kStateSolid, temperature=SATELLITE_TEMP)
AddElement(al, alElt, 1.0)
alPt = G4MaterialPropertiesTable()
# DO NOT ADD JUST RINDEX!!!!!!!!!!!!!!!!!!!1
# IT WILL CAUSE PROGRAM TO CRASH IF YOU PUT A SKIN AROUND THE MATERIAL
# AddProperty(alPt, "RINDEX", AL_REFR_IDX_ENERGIES, AL_REFR_IDX_REAL)
AddProperty(alPt, "REALRINDEX", AL_REFR_IDX_ENERGIES, AL_REFR_IDX_REAL)
AddProperty(alPt, "IMAGINARYRINDEX", AL_REFR_IDX_ENERGIES, AL_REFR_IDX_IMAG)
SetMaterialPropertiesTable(al, move!(alPt))
det.alMat = CxxPtr(al)

#---beryllium-----------------------------------------------------------------------------------
beElt = FindOrBuildElement(nist, "Be")
be = G4Material("beryllium", density=BE_DENSITY, ncomponents=1, state=kStateSolid, temperature=SATELLITE_TEMP)
AddElement(be, beElt, 1)
bePt = G4MaterialPropertiesTable()
AddProperty(bePt, "RINDEX", BE_REFR_IDX_ENERGIES, BE_REFR_IDX_REAL)
AddProperty(bePt, "REALRINDEX", BE_REFR_IDX_ENERGIES, BE_REFR_IDX_REAL)
AddProperty(bePt, "IMAGINARYRINDEX", BE_REFR_IDX_ENERGIES, BE_REFR_IDX_IMAG)
SetMaterialPropertiesTable(be, move!(bePt))
det.beMat = CxxPtr(be)

return

end

function scintConstruct(det::ScintDetector)::CxxPtr{G4VPhysicalVolume} (; worldSize, doTeflon, doAluminum, cryssize, siside, sithick, tefthick, althick, airgap, checkOverlaps) = det

#---Materials----------------------------------------------------------------------------------
defineMaterials!(det)

#---World--------------------------------------------------------------------------------------
worldBox = G4Box("World", 0.5*worldSize, 0.5*worldSize, 0.5*worldSize)        
worldLogVol = G4LogicalVolume(worldBox, det.vacMat, "World")
worldVis = G4VisAttributes(true, G4Colour(1, 1, 1, 0.05))
SetVisAttributes(worldLogVol, worldVis)
worldPlacement = G4PVPlacement(nothing, G4ThreeVector(), worldLogVol, "World", nothing, false, 0, checkOverlaps)

#---cryst--------------------------------------------------------------------------------------
crystBox = G4Box("cebr3_box", crys_size/2, crys_size/2, crys_size/2)
crystLogVol = G4LogicalVolume(crystBox, det.cebr3Mat, "cebr3_log")
crystVis = G4VisAttributes(true, G4Colour(0.35, 0.5, 0.92, 0.8))
SetVisAttributes(crystLogVol, crystVis)
crystPlacement = G4PVPlacement(nothing, G4ThreeVector(), crystLogVol, "cebr3_phys", worldLogVol, false, 0, checkOverlaps)

surf = G4OpticalSurface("cebr3_optical_edge")
# See G4 documentation on UNIFIED model
SetType(surf, dielectric_LUTDAVIS)
SetModel(surf, DAVIS)
SetFinish(surf, Rough_LUT);
G4LogicalBorderSurface("cebr3_logical_border_surf", CxxPtr(crystPlacement), CxxPtr(worldPlacement), CxxPtr(surf))

#---teflon-------------------------------------------------------------------------------------
if doTeflon
    precutHalfThick = (air_gap + tef_thick + crys_size)/2
    preCutPtfeBox = G4Box("precut_ptfe", precutHalfThick, precutHalfThick, precutHalfThick)
    addAirGapHalfXyz = 0.5 * (crys_size + air_gap)

    crystPlusAirGap = G4Box("", addAirGapHalfXyz, addAirGapHalfXyz, addAirGapHalfXyz)
    translateSubtract = G4ThreeVector(0, 0, 0)
    slicedPtfe = G4SubtractionSolid("sliced_ptfe", move!(preCutPtfeBox), move!(crystPlusAirGap), CxxPtr{G4RotationMatrix}(C_NULL), translateSubtract)
    translateSubtract = G4ThreeVector(0, 0, 1.8*cm)
    cutCap = G4Box("", precutHalfThick + 5*mm, precutHalfThick + 5*mm, precutHalfThick)
    slicedPtfeOpen = G4SubtractionSolid("sliced_ptfe_open", move!(slicedPtfe), move!(cutCap), CxxPtr{G4RotationMatrix}(C_NULL), translateSubtract)
    ptfeLogVol = G4LogicalVolume(slicedPtfeOpen, det.tefMat, "ptfe_log")
    tefVis = G4VisAttributes(true, G4Colour(0, 1, 0, 0.1))
    SetVisAttributes(ptfeLogVol, tefVis)

    # attachTeflonOpticalSurf
    tefSuf = G4OpticalSurface("teflon_optical_surf")
    SetType(tefSuf, dielectric_dielectric)
    SetModel(tefSuf, unified)
    SetFinish(tefSuf, groundfrontpainted)
    SetSigmaAlpha(tefSuf, 0.)
    SetMaterialPropertiesTable(tefSuf, GetMaterialPropertiesTable(det.tefMat))
    G4LogicalSkinSurface("teflon_skin_surf", CxxPtr(ptfeLogVol), CxxPtr(tefSuf))
    
    G4PVPlacement(nothing, G4ThreeVector(), ptfeLogVol, "ptfe_phys", worldLogVol, false, 0, checkOverlaps)
end

#---aluminum-------------------------------------------------------------------------------------
if doAluminum
    sliceCapThick = 0.5 * (tef_thick + crys_size + al_thick + 2*air_gap) + 5*mm
    precutHalfThick = sliceCapThick - 5*mm
    sliceOutHalfThick = precutHalfThick - 0.5 * al_thick
  
    precut = G4Box("", precutHalfThick, precutHalfThick, precutHalfThick)
    cutThisOut = G4Box("", sliceOutHalfThick, sliceOutHalfThick, sliceOutHalfThick)
    cutOffEnd = G4Box("", sliceCapThick, sliceCapThick, 6*mm)
    hollow = G4SubtractionSolid("hollow_al", move!(precut), move!(cutThisOut), CxxPtr{G4RotationMatrix}(C_NULL), G4ThreeVector())
    translateSubtract = G4ThreeVector(0, 0, sliceCapThick)
    open = G4SubtractionSolid("hollow_open_al", move!(hollow), move!(cutOffEnd), CxxPtr{G4RotationMatrix}(C_NULL), translateSubtract)
    
    alLogVol = G4LogicalVolume(open, det.alMat, "al_log")
    alVis = G4VisAttributes(true, G4Colour(1, 0., 0., 0.1))
    SetVisAttributes(alLogVol, alVis)

    # attachAlOpticalSurface(alLogVol)
    alSurf = G4OpticalSurface("al_optical_surf")
    SetModel(alSurf, unified)
    SetType(alSurf, dielectric_metal)
    SetFinish(alSurf, polished)
    SetMaterialPropertiesTable(alSurf, GetMaterialPropertiesTable(det.alMat))
    G4LogicalSkinSurface("al_skin_surf", CxxPtr(alLogVol), CxxPtr(alSurf))
    G4PVPlacement(nothing, G4ThreeVector(), alLogVol, "al_phys", worldLogVol, false, 0, checkOverlaps)
end

#---sillicon-----------------------------------------------------------------------------------
siBox = G4Box("si_box", si_side/2, si_side/2, si_thick/2)
siLogVol = G4LogicalVolume(siBox, det.siMat, "si_log")
siVis = G4VisAttributes(true, G4Colour(0, 0, 1, 0.5))
SetVisAttributes(siLogVol, siVis)

translate = G4ThreeVector(0, 0, crys_size/2 + si_thick/2)
G4PVPlacement(nothing, translate, siLogVol, "si_phys", worldLogVol, false, 0, checkOverlaps)

siSurf = G4OpticalSurface("si_surf")
SetModel(siSurf, unified)
SetFinish(siSurf, polished)
SetType(siSurf, dielectric_dielectric)
SetMaterialPropertiesTable(siSurf, GetMaterialPropertiesTable(det.siMat))
G4LogicalSkinSurface("si_skin", CxxPtr(siLogVol), CxxPtr(siSurf))

#  return the world
return worldPlacement

end

Geant4.getConstructor(::ScintDetector)::Function = scintConstruct```