Detector.jl

#--------------------------------------------------------------------------------------------------
# 
#--------------------------------------------------------------------------------------------------
mutable struct ScintDetector <: G4JLDetector
    # main input parameters
    const worldSize::Float64
    const doTeflon::Bool
    const doAluminum::Bool
    const crys_size::Float64
    const si_side::Float64
    const si_thick::Float64
    const tef_thick::Float64
    const al_thick::Float64
    const air_gap::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,
                             crys_size = 2cm,
                             si_side = 1cm,
                             si_thick = 2mm,
                             tef_thick = 1mm,
                             al_thick = 1mm,
                             air_gap = 10mm,
                             checkOverlaps = true )
        self = new(worldSize, doTeflon, doAluminum, crys_size, si_side, si_thick, tef_thick, al_thick, air_gap, 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, crys_size, si_side, si_thick, tef_thick, al_thick, air_gap, 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