@@ -27,6 +27,7 @@ If [LegendHDF5IO.jl](https://github.com/legend-exp/LegendHDF5IO.jl) is loaded, t
2727* `self_repulsion::Bool = false`: Activate or deactive self-repulsion of charge carriers of the same type.
2828* `number_of_carriers::Int = 1`: Number of charge carriers to be used in the N-Body simulation of an energy deposition.
2929* `number_of_shells::Int = 1`: Number of shells around the `center` point of the energy deposition.
30+ * `max_interaction_distance = NaN`: Maximum distance for which charge clouds will interact with each other (if `NaN`, then all charge clouds drift independently).
3031* `verbose = false`: Activate or deactivate additional info output.
3132* `chunk_n_physics_events::Int = 1000` (`LegendHDF5IO` only): Number of events that should be saved in a single HDF5 output file.
3233
@@ -53,6 +54,7 @@ function simulate_waveforms( mcevents::TypedTables.Table, sim::Simulation{T};
5354 self_repulsion:: Bool = false ,
5455 number_of_carriers:: Int = 1 ,
5556 number_of_shells:: Int = 1 ,
57+ max_interaction_distance:: Union{<:Real, <:LengthQuantity} = NaN ,
5658 verbose:: Bool = false ) where {T <: SSDFloat }
5759 n_total_physics_events = length (mcevents)
5860 Δtime = T (to_internal_units (Δt))
@@ -71,7 +73,7 @@ function simulate_waveforms( mcevents::TypedTables.Table, sim::Simulation{T};
7173
7274 # First simulate drift paths
7375 drift_paths_and_edeps = _simulate_charge_drifts (mcevents, sim, Δt, max_nsteps, electric_field,
74- diffusion, self_repulsion, number_of_carriers, number_of_shells, verbose)
76+ diffusion, self_repulsion, number_of_carriers, number_of_shells, max_interaction_distance, verbose)
7577 drift_paths = map (x -> vcat ([vcat (ed... ) for ed in x[1 ]]. .. ), drift_paths_and_edeps)
7678 edeps = map (x -> vcat ([vcat (ed... ) for ed in x[2 ]]. .. ), drift_paths_and_edeps)
7779 # now iterate over contacts and generate the waveform for each contact
@@ -94,19 +96,40 @@ function simulate_waveforms( mcevents::TypedTables.Table, sim::Simulation{T};
9496 return vcat (mcevents_chns... )
9597end
9698
97- _convertEnergyDepsToChargeDeps (pos:: AbstractVector{<:SVector{3}} , edep:: AbstractVector{<:Quantity} , det:: SolidStateDetector ; kwargs... ) =
98- _convertEnergyDepsToChargeDeps ([[p] for p in pos], [[e] for e in edep], det; kwargs... )
99+ function _convertEnergyDepsToChargeDeps (
100+ pos:: AbstractVector{<:SVector{3}} , edep:: AbstractVector{<:Quantity} , det:: SolidStateDetector{T} ;
101+ particle_type:: Type{PT} = Gamma,
102+ radius:: Vector{<:Union{<:Real, <:LengthQuantity}} = radius_guess .(T .(to_internal_units .(edep)), particle_type),
103+ max_interaction_distance:: Union{<:Real, <:LengthQuantity} = NaN ,
104+ kwargs...
105+ ) where {T <: SSDFloat , PT <: ParticleType }
106+
107+ @assert eachindex (pos) == eachindex (edep) == eachindex (radius)
108+
109+ d:: T = T (to_internal_units (max_interaction_distance))
110+ @assert isnan (d) || d >= 0 " Max. interaction distance must be positive or NaN (no grouping), but $(max_interaction_distance) was given."
111+
112+ loc, ene, rad = group_points_by_distance (CartesianPoint .(map (x -> to_internal_units .(x), pos)), T .(to_internal_units .(edep)), T .(to_internal_units .(radius)), d)
113+ _convertEnergyDepsToChargeDeps (loc, ene, det; radius = rad, kwargs... )
114+ end
115+
116+ function _convertEnergyDepsToChargeDeps (
117+ pos:: AbstractVector{<:AbstractVector} , edep:: AbstractVector{<:AbstractVector} , det:: SolidStateDetector{T} ;
118+ particle_type:: Type{PT} = Gamma,
119+ radius:: AbstractVector{<:AbstractVector{<:Union{<:Real, <:LengthQuantity}}} = map (e -> radius_guess .(to_internal_units .(e), particle_type), edep),
120+ number_of_carriers:: Int = 1 , number_of_shells:: Int = 1 ,
121+ max_interaction_distance:: Union{<:Real, <:LengthQuantity} = NaN # is ignored here
122+ ) where {T <: SSDFloat , PT <: ParticleType }
99123
100- function _convertEnergyDepsToChargeDeps (pos:: AbstractVector{<:AbstractVector} , edep:: AbstractVector{<:AbstractVector} , det:: SolidStateDetector ;
101- number_of_carriers:: Int = 1 , number_of_shells:: Int = 1 )
102124 charge_clouds = broadcast (
103125 iEdep_indep -> broadcast (
104126 i_together -> begin
105127 nbcc = NBodyChargeCloud (
106- CartesianPoint (to_internal_units .( (pos[iEdep_indep][i_together]))),
107- edep[iEdep_indep][i_together],
128+ CartesianPoint (T .( to_internal_units . (pos[iEdep_indep][i_together]))),
129+ T .( to_internal_units ( edep[iEdep_indep][i_together])) ,
108130 number_of_carriers,
109- number_of_shells = number_of_shells
131+ number_of_shells = number_of_shells,
132+ radius = T (to_internal_units (edep[iEdep_indep][i_together]))
110133 )
111134 move_charges_inside_semiconductor! ([nbcc. locations], [nbcc. energies], det)
112135 nbcc
@@ -115,8 +138,8 @@ function _convertEnergyDepsToChargeDeps(pos::AbstractVector{<:AbstractVector}, e
115138 ),
116139 eachindex (edep)
117140 )
118- locations = [ map (cc -> cc. locations, ccs) for ccs in charge_clouds]
119- edeps = [ map (cc -> cc. energies, ccs) for ccs in charge_clouds]
141+ locations = map . (cc -> cc. locations, charge_clouds)
142+ edeps = map . (cc -> cc. energies, charge_clouds)
120143 locations, edeps
121144end
122145
@@ -127,9 +150,10 @@ function _simulate_charge_drifts( mcevents::TypedTables.Table, sim::Simulation{T
127150 self_repulsion:: Bool ,
128151 number_of_carriers:: Int ,
129152 number_of_shells:: Int ,
153+ max_interaction_distance:: Union{<:Real, <:LengthQuantity} ,
130154 verbose:: Bool ) where {T <: SSDFloat }
131155 @showprogress map (mcevents) do phyevt
132- locations, edeps = _convertEnergyDepsToChargeDeps (phyevt. pos, phyevt. edep, sim. detector; number_of_carriers, number_of_shells)
156+ locations, edeps = _convertEnergyDepsToChargeDeps (phyevt. pos, phyevt. edep, sim. detector; number_of_carriers, number_of_shells, max_interaction_distance )
133157 drift_paths = map ( i -> _drift_charges (sim. detector, sim. electric_field. grid, sim. point_types,
134158 VectorOfArrays (locations[i]), VectorOfArrays (edeps[i]),
135159 electric_field, T (Δt. val) * unit (Δt), max_nsteps = max_nsteps,
0 commit comments