Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
624a869
Function to define CylindricalPoint with units
claudiaalvgar Nov 10, 2025
cb2e603
Allow NBodyChargeCloud to receive CylindricalPoint in Floats and with…
claudiaalvgar Nov 10, 2025
fa00d52
Use the defined function to_internal_point to convert Points to Carte…
claudiaalvgar Nov 10, 2025
1f999ac
Fix manual with new behaviour
claudiaalvgar Nov 10, 2025
9482e5d
convert CartesianPoint to SVector to be returned from run_geant4_simu…
claudiaalvgar Nov 12, 2025
80be3b4
add test for bug found
claudiaalvgar Nov 12, 2025
5b3db1b
pass N and unify functions
claudiaalvgar Nov 12, 2025
9bfe583
restrict Unitful.Quantity
claudiaalvgar Nov 12, 2025
035398f
implemented suggestions
claudiaalvgar Nov 12, 2025
652d324
export to_internal_point function
claudiaalvgar Nov 12, 2025
2f0981d
Add tests for new NBodyChargeCloud functions
claudiaalvgar Nov 12, 2025
67343ad
comment erro line
claudiaalvgar Nov 13, 2025
6d6c103
add test
claudiaalvgar Nov 13, 2025
1df5e98
unify functions, just 1 function failed
claudiaalvgar Nov 13, 2025
c6a34e2
add functions to to_internal_units
claudiaalvgar Nov 13, 2025
09a64e3
added suggested changes
claudiaalvgar Nov 13, 2025
8d6268d
delete export to_internal_point
claudiaalvgar Nov 13, 2025
a266d8d
delete ucovert
claudiaalvgar Nov 13, 2025
ebe8db6
Add `LegendHDF5IO` support for `Cartesian/CylindricalPoint`
fhagemann Nov 14, 2025
a6a74a1
Revert "comment erro line"
fhagemann Nov 14, 2025
023d2cf
Revert "convert CartesianPoint to SVector to be returned from run_gea…
fhagemann Nov 14, 2025
a4dd5fe
put back Cylindrical test with units
claudiaalvgar Nov 14, 2025
f000ab8
change CylindricalPoint function and delete function to_internal_unit…
claudiaalvgar Nov 14, 2025
5fe6418
Update stripping units of `CylindricalPoint`
fhagemann Nov 15, 2025
253ec30
Add tests for units/type promotion of `CylindricalPoint`
fhagemann Nov 15, 2025
70b277b
convert cluster_radius to internal units
claudiaalvgar Nov 25, 2025
4416ac5
Add CartesianPoint constructor to avoid units
claudiaalvgar Nov 25, 2025
8e25960
uncomment tests
claudiaalvgar Nov 25, 2025
ddaa78c
remove units from test
claudiaalvgar Nov 25, 2025
366581b
add back units to pos
claudiaalvgar Nov 27, 2025
1f2eb34
add tests
claudiaalvgar Nov 27, 2025
f1e85c6
remove units
claudiaalvgar Nov 27, 2025
a700a07
account for suggestions
claudiaalvgar Nov 27, 2025
49c900c
Suggested changes and fix doc without units
claudiaalvgar Nov 28, 2025
efce348
No need to deal with units, the plot recipe will deal with this
fhagemann Nov 28, 2025
f6261ca
Strip position units in `simulate_waveforms` further down
fhagemann Nov 28, 2025
861e5db
Add test for different types of `pos` in `simulate_waveforms`
fhagemann Nov 28, 2025
cfba8d4
Ensure cluster radius is charge clustering has correct units
fhagemann Nov 28, 2025
92fedb0
Avoid converting `CartesianPoint` to `SVector` when writing to HDF5
fhagemann Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/src/man/charge_drift.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ using SolidStateDetectors #hide
using Unitful #hide
using Plots #hide
T = Float64 #hide
center = CartesianPoint{T}([0,0,0])
center = CartesianPoint{T}(0,0,0)
energy = 1460u"keV"
nbcc = NBodyChargeCloud(center, energy)
plot(nbcc)
Expand All @@ -428,7 +428,7 @@ plot(nbcc, color = :red, size = (500,500), xlims = (-0.0012, 0.0012), ylims = (-
For an [`NBodyChargeCloud`](@ref) consisting of more than around 50 charges, the shells should consist of more than 20 point charges and the approach with using [Platonic Solids](@ref) for the shell structure might not be favored anymore. For this, a [second algorithm](https://www.cmu.edu/biolphys/deserno/pdf/sphere_equi.pdf) was implemented that generates point charges equally distributed on the surface of a regular sphere. The approximate number of charges needs to be passed to the constructor of [`NBodyChargeCloud`](@ref) to use this method.

````@example NBodyChargeCloud
center = CartesianPoint{T}([0,0,0])
center = CartesianPoint{T}(0,0,0)
energy = 1460u"keV"
nbcc = NBodyChargeCloud(center, energy, 100)
plot(nbcc)
Expand All @@ -441,7 +441,7 @@ plot(nbcc, color = :red, size = (500,500), xlims = (-0.0012, 0.0012), ylims = (-
Diffusion describes the random thermal motion of charge carriers. In SolidStateDetectors.jl, diffusion is simulated using a random walk algorithm. Diffusion is simulated using fixed-step vectors where the magnitude of the step vectors depends on the diffusion constant $D$ and the time steps $\Delta t$.

```julia
center = CartesianPoint{T}([0,0,0])
center = CartesianPoint{T}(0,0,0)
energy = 1460u"keV"
nbcc = NBodyChargeCloud(center, energy, 100)
evt = Event(nbcc)
Expand Down Expand Up @@ -478,7 +478,7 @@ After the creation electron-hole pairs, both the electron and the hole clouds re
SolidStateDetectors.jl does not account for attraction of electron and holes but only for repulsion of charge carriers of the same type. The determination of the electric field vector is calculated pair-wise for each pair of charge carriers.

```julia
center = CartesianPoint{T}([0,0,0])
center = CartesianPoint{T}(0,0,0)
energy = 1460u"keV"
nbcc = NBodyChargeCloud(center, energy, 100)
evt = Event(nbcc)
Expand All @@ -491,7 +491,7 @@ simulate!(evt, sim, self_repulsion = true)

[Diffusion](@ref) and [Self-Repulsion](@ref) can be simulated both at once to get the most realistic picture:
```julia
center = CartesianPoint{T}([0,0,0])
center = CartesianPoint{T}(0,0,0)
energy = 1460u"keV"
nbcc = NBodyChargeCloud(center, energy, 100)
evt = Event(nbcc)
Expand Down
95 changes: 95 additions & 0 deletions ext/SolidStateDetectorsLegendHDF5IOExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import ..LegendHDF5IO

using ..SolidStateDetectors
using ..SolidStateDetectors: RealQuantity, SSDFloat, to_internal_units, chunked_ranges, LengthQuantity
using ..SolidStateDetectors.ConstructiveSolidGeometry: AbstractCoordinatePoint

using ArraysOfArrays
import Tables
using TypedTables, Unitful, UnitfulAtomic
Expand Down Expand Up @@ -65,4 +67,97 @@ function SolidStateDetectors.simulate_waveforms( mcevents::AbstractVector{<:Name
end
end


# Add support for reading and writing CartesianPoint and CylindricalPoint using LegendHDF5IO
function __init__()
LegendHDF5IO._datatype_dict["cartesianpoint"] = CartesianPoint
LegendHDF5IO._datatype_dict["cylindricalpoint"] = CylindricalPoint
end

LegendHDF5IO.datatype_to_string(::Type{<:CartesianPoint}) = "cartesianpoint"
LegendHDF5IO.datatype_to_string(::Type{<:CylindricalPoint}) = "cylindricalpoint"



# support writing points to HDF5 file using LH5Array
function LegendHDF5IO.create_entry(parent::LegendHDF5IO.LHDataStore, name::AbstractString,
pt::T; kwargs...) where {T <: AbstractCoordinatePoint}
LegendHDF5IO.create_entry(parent, name, getindex.(Ref(pt), 1:3); kwargs...)
LegendHDF5IO.setdatatype!(parent.data_store[name], T)
nothing
end

function LegendHDF5IO.LH5Array(ds::LegendHDF5IO.HDF5.Dataset, T::Type{<:AbstractCoordinatePoint})
T(read(ds) .* LegendHDF5IO.getunits(ds)...)
end

# support writing arrays of points to HDF5 file using LH5Array
function LegendHDF5IO.create_entry(parent::LegendHDF5IO.LHDataStore, name::AbstractString,
pts::T; kwargs...) where {T <: AbstractVector{<:AbstractCoordinatePoint}}
LegendHDF5IO.create_entry(parent, name, VectorOfVectors([getindex.(Ref(pt), 1:3) for pt in pts]); kwargs...)
LegendHDF5IO.setdatatype!(parent.data_store[name], T)
nothing
end

function LegendHDF5IO.LH5Array(ds::LegendHDF5IO.HDF5.Group, T::Type{<:AbstractVector{<:CartesianPoint}})
data = LegendHDF5IO.LH5Array(ds, VectorOfVectors)
Vector([CartesianPoint(d...) for d in data])
end

function LegendHDF5IO.LH5Array(ds::LegendHDF5IO.HDF5.Group, T::Type{<:AbstractVector{<:CylindricalPoint}})
data = LegendHDF5IO.LH5Array(ds, VectorOfVectors)
Vector([CylindricalPoint(d...) for d in data])
end

# support writing points to HDF5 file using readdata/writedata
function LegendHDF5IO.writedata(
output::LegendHDF5IO.HDF5.H5DataStore, name::AbstractString,
pt::AbstractCoordinatePoint,
fulldatatype::DataType = typeof(pt)
)
data = getindex.(Ref(pt), 1:3)
LegendHDF5IO.writedata(output, name, data, fulldatatype)
end

function LegendHDF5IO.readdata(
input::LegendHDF5IO.HDF5.H5DataStore, name::AbstractString,
fulldatatype::Type{T}
) where {T <: AbstractCoordinatePoint}
data = LegendHDF5IO.readdata(input, name, Vector)
T(data...)
end


# support writing arrays of points to HDF5 file using readdata/writedata
function LegendHDF5IO.writedata(
output::LegendHDF5IO.HDF5.H5DataStore, name::AbstractString,
pts::AbstractVector{<:AbstractCoordinatePoint},
fulldatatype::DataType = typeof(pts)
)
data = VectorOfVectors([getindex.(Ref(pt), 1:3) for pt in pts])
LegendHDF5IO.writedata(output, name, data, fulldatatype)
LegendHDF5IO.setdatatype!(output[name], fulldatatype)
nothing
end

function LegendHDF5IO.readdata(
input::LegendHDF5IO.HDF5.H5DataStore, name::AbstractString,
fulldatatype::Type{T}
) where {T <:AbstractVector{<:CartesianPoint}}
data = LegendHDF5IO.readdata(input, name, VectorOfVectors)
output = [CartesianPoint(d...) for d in data]
@assert output isa T
output
end

function LegendHDF5IO.readdata(
input::LegendHDF5IO.HDF5.H5DataStore, name::AbstractString,
fulldatatype::Type{T}
) where {T <:AbstractVector{<:CylindricalPoint}}
data = LegendHDF5IO.readdata(input, name, VectorOfVectors)
output = [CylindricalPoint(d...) for d in data]
@assert output isa T
output
end

end # module LegendHDF5IO
14 changes: 13 additions & 1 deletion src/ChargeCloudModels/ChargeCloudModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ NBodyChargeCloud(center, energy, number_of_shells = 3, shell_structure = SolidSt
!!! note
Using values with units for `energy` requires the package [Unitful.jl](https://github.com/JuliaPhysics/Unitful.jl).
"""
function NBodyChargeCloud(center::Union{<:CartesianPoint{<:Unitful.Quantity},<:CylindricalPoint{<:Union{SSDFloat, Unitful.Quantity}}},
energy::RealQuantity; kwargs...)
internal_center = to_internal_units(center)
return NBodyChargeCloud(internal_center, energy; kwargs...)
end

function NBodyChargeCloud(center::CartesianPoint{T}, energy::RealQuantity, particle_type::Type{PT} = Gamma;
radius::T = radius_guess(T(to_internal_units(energy)), particle_type), number_of_shells::Int = 2, shell_structure = Dodecahedron
)::NBodyChargeCloud{T, number_of_shells, typeof(shell_structure{T})} where {T, PT <: ParticleType}
Expand Down Expand Up @@ -121,6 +127,12 @@ NBodyChargeCloud(center, energy, 200, number_of_shells = 3)
!!! note
Using values with units for `energy` requires the package [Unitful.jl](https://github.com/JuliaPhysics/Unitful.jl).
"""
function NBodyChargeCloud(center::Union{<:CartesianPoint{<:Unitful.Quantity},<:CylindricalPoint{<:Union{SSDFloat, Unitful.Quantity}}},
energy::RealQuantity, N::Integer; kwargs...)
internal_center = to_internal_units(center)
return NBodyChargeCloud(internal_center, energy, N; kwargs...)
end

function NBodyChargeCloud(center::CartesianPoint{T}, energy::RealQuantity, N::Integer, particle_type::Type{PT} = Gamma;
radius::T = radius_guess(T(to_internal_units(energy)), particle_type), number_of_shells::Int = 2,
)::NBodyChargeCloud{T, number_of_shells, NTuple{N > 1 ? number_of_shells : 0, Int}} where {T, PT <: ParticleType}
Expand All @@ -142,4 +154,4 @@ function NBodyChargeCloud(center::CartesianPoint{T}, energy::RealQuantity, N::In
end

return NBodyChargeCloud{T, number_of_shells, NTuple{length(n), Int}}( locations, energies./sum(energies) * T(to_internal_units(energy)), Tuple(n))
end
end
3 changes: 1 addition & 2 deletions src/ChargeClustering/ChargeClustering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ function cluster_detector_hits(
r_edep = similar(edep, 0)
r_pos = similar(pos, 0)

posunit = unit(PT)
ustripped_cradius = ustrip(posunit, cluster_radius)
ustripped_cradius = to_internal_units(cluster_radius)

for d_hits_nt in grouped
d_hits = TypedTables.Table(d_hits_nt)
Expand Down
65 changes: 57 additions & 8 deletions src/ConstructiveSolidGeometry/PointsAndVectors/Points.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ function Base.isapprox(a::AbstractCartesianPoint, b::AbstractCartesianPoint; kwa
isapprox(get_z(a), get_z(b); kwargs...)
end

function to_internal_units(x::AbstractCartesianPoint)
CartesianPoint(to_internal_units(get_x(x)), to_internal_units(get_y(x)), to_internal_units(get_z(x)))
end



# Unitful uses the `*` and `/` operators to combine values with units. But mathematically that's really not an algebraic
# product (not defined for affine points), but a cartesian product, so supporting this should be fine:
Base.:(*)(pt::AbstractCartesianPoint{<:Real}, u::Unitful.Units{<:Any,Unitful.𝐋}) = CartesianPoint(get_x(pt) * u, get_y(pt) * u, get_z(pt) * u)
Expand Down Expand Up @@ -72,6 +66,22 @@ function CartesianPoint(x::TX, y::TY, z::TZ) where {TX<:Real,TY<:Real,TZ<:Real}
CartesianPoint{T}(T(x),T(y),T(z))
end

#Unit support
function CartesianPoint(x::Unitful.Quantity, y::Unitful.Quantity, z::Unitful.Quantity)
for (i, pt) in enumerate((x, y, z))
!(pt isa Unitful.Length) && throw(ArgumentError(
"Coordinate $i must be a length, got $(pt) with unit $(Unitful.unit(pt))"
))
end
CartesianPoint(x::Unitful.Length, y::Unitful.Length, z::Unitful.Length)
end

function CartesianPoint(x::Unitful.Length, y::Unitful.Length, z::Unitful.Length)
vals = to_internal_units.( (x, y, z) )
T = all(v -> v isa Float32, vals) ? Float32 : Float64
CartesianPoint{T}(T.(vals)...)
end

CartesianPoint(; x = 0, y = 0, z = 0) = CartesianPoint(x, y, z)

CartesianPoint{T}(;x = 0, y = 0, z = 0) where {T} = CartesianPoint{T}(T(x),T(y),T(z))
Expand Down Expand Up @@ -204,7 +214,8 @@ end
@inline Base.zero(::CartesianZero{T}) where {T} = CartesianZero{T}()
@inline Base.iszero(::CartesianZero) = true

Base.:(*)(::CartesianZero{T}, u::Unitful.Units{<:Any,Unitful.𝐋}) where {T<:Real} = CartesianZero{Quantity{T, Unitful.𝐋, typeof(u)}}()
#Base.:(*)(::CartesianZero{T}, u::Unitful.Units{<:Any,Unitful.𝐋}) where {T<:Real} = CartesianZero{Quantity{T, Unitful.𝐋, typeof(u)}}()
Base.:*(z::CartesianZero, u::Unitful.Quantity) = z

function Unitful.uconvert(u::Unitful.Units{T,Unitful.NoDims}, pt::CartesianZero{<:Quantity{U, Unitful.NoDims}}) where {T,U}
CartesianZero{promote_type(T,U)}()
Expand Down Expand Up @@ -244,6 +255,27 @@ struct CylindricalPoint{T} <: AbstractCylindricalPoint{T}
CylindricalPoint{T}(r::Real, φ::Real, z::Real) where {T} = new(T(r), mod(T(φ),T(2π)), T(z))
end

#Units support
function CylindricalPoint(r, φ, z)
if !(r isa Real || r isa Unitful.Length)
throw(ArgumentError("Expected `r` to be a length or Real, got unit $(Unitful.unit(r))"))
end

if !(φ isa Real || φ isa Unitful.Quantity{<:Real, NoDims})
throw(ArgumentError("Expected `φ` to be an angle or Real, got unit $(Unitful.unit(φ))"))
end

if !(z isa Real || z isa Unitful.Length)
throw(ArgumentError("Expected `z` to be a length or Real, got unit $(Unitful.unit(z))"))
end

r_val = to_internal_units(r)
φ_val = to_internal_units(φ)
z_val = to_internal_units(z)

return CylindricalPoint(r_val, φ_val, z_val)
end

function CylindricalPoint(r::TR, φ::TP, z::TZ) where {TR<:Real,TP<:Real,TZ<:Real}
# ToDo: Simplify this:
eltypes = _csg_get_promoted_eltype.((TR,TP,TZ))
Expand Down Expand Up @@ -303,4 +335,21 @@ Base.iszero(pt::CylindricalPoint) = iszero(pt.r) && iszero(pt.z)
@inline Base.:(-)(a::CylindricalPoint, b::CylindricalPoint) = CartesianPoint(a) - CartesianPoint(b)

@inline Base.transpose(pt::CylindricalPoint) = CylindricalPoint(transpose(pt.r), transpose(pt.φ), transpose(pt.z))
@inline Base.adjoint(pt::CylindricalPoint) = CylindricalPoint(adjoint(pt.r), adjoint(pt.φ), adjoint(pt.z))

@inline Base.adjoint(pt::CylindricalPoint) = CylindricalPoint(adjoint(pt.r), adjoint(pt.φ), adjoint(pt.z))

function to_internal_units(pt::CartesianPoint)
CartesianPoint(to_internal_units(pt.x), to_internal_units(pt.y), to_internal_units(pt.z))
end

function to_internal_units(pt::CylindricalPoint)
_r = to_internal_units(pt.r)
_φ = to_internal_units(pt.φ)
_z = to_internal_units(pt.z)

return CartesianPoint(_r * cos(_φ), _r * sin(_φ), _z)
end

function to_internal_units(pt::AbstractCoordinatePoint)
error("Unsupported point type $(typeof(pt)). Expected CartesianPoint or CylindricalPoint.")
end
55 changes: 33 additions & 22 deletions src/Event/Event.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
mutable struct Event{T <: SSDFloat}

mutable struct Event{T <: SSDFloat}
Collection struct for individual events.
This (mutable) struct is meant to be used to look at individual events,
not to process a huge amount of events.
Expand All @@ -19,30 +19,40 @@ mutable struct Event{T <: SSDFloat}
Event{T}() where {T <: SSDFloat} = new{T}(VectorOfArrays(Vector{CartesianPoint{T}}[]), VectorOfArrays(Vector{T}[]), missing, missing)
end

function Event(location::AbstractCoordinatePoint{T}, energy::RealQuantity = one(T))::Event{T} where {T <: SSDFloat}
function Event(location::AbstractCoordinatePoint, energy::RealQuantity = 1)
pt_internal = to_internal_units(location)
T = eltype(pt_internal)
evt = Event{T}()
evt.locations = VectorOfArrays([[CartesianPoint(location)]])
evt.energies = VectorOfArrays([[T(to_internal_units(energy))]])
evt.locations = VectorOfArrays([[pt_internal]])
evt.energies = VectorOfArrays([[T(to_internal_units(energy))]])
return evt
end

function Event(locations::Vector{<:AbstractCoordinatePoint{T}}, energies::Vector{<:RealQuantity} = ones(T, length(locations)); max_interaction_distance::Union{<:Real, <:LengthQuantity} = NaN)::Event{T} where {T <: SSDFloat}
function Event(locations::Vector{<:AbstractCoordinatePoint}, energies::Vector{<:RealQuantity} = fill(1, length(locations)); max_interaction_distance::Union{<:Real, <:LengthQuantity} = NaN)
pts_internal = to_internal_units.(locations)
T = eltype(first(pts_internal))
d::T = T(to_internal_units(max_interaction_distance))
@assert isnan(d) || d >= 0 "Max. interaction distance must be positive or NaN (no grouping), but $(max_interaction_distance) was given."
evt = Event{T}()

if isnan(d) # default: no grouping, the charges from different hits drift independently
evt.locations = VectorOfArrays(broadcast(pt -> [CartesianPoint(pt)], locations))
evt.energies = VectorOfArrays(broadcast(E -> [T(to_internal_units(E))], energies))
evt.locations = VectorOfArrays(broadcast(pt -> [pt], pts_internal))
evt.energies = VectorOfArrays(broadcast(E -> [T(to_internal_units(E))], energies))
else
evt.locations, evt.energies = group_points_by_distance(CartesianPoint.(locations), T.(to_internal_units.(energies)), d)
evt.locations, evt.energies = group_points_by_distance(pts_internal, T.(to_internal_units.(energies)), d)
end
return evt
end

function Event(locations::Vector{<:Vector{<:AbstractCoordinatePoint{T}}}, energies::Vector{<:Vector{<:RealQuantity}} = [[one(T) for i in j] for j in locations])::Event{T} where {T <: SSDFloat}
function Event(locations::Vector{<:Vector{<:AbstractCoordinatePoint}}, energies::Vector{<:Vector{<:RealQuantity}} = [[1 for _ in group] for group in locations])

T = eltype(to_internal_units(first(first(locations))))
locs = [[convert(CartesianPoint{T}, to_internal_units(pt)) for pt in group] for group in locations]
ens = [[T(to_internal_units(E)) for E in group] for group in energies]

evt = Event{T}()
evt.locations = VectorOfArrays(broadcast(pts -> CartesianPoint{T}.(pts), locations))
evt.energies = VectorOfArrays(Vector{T}.(to_internal_units.(energies)))
evt.locations = VectorOfArrays(locs)
evt.energies = VectorOfArrays(ens)
return evt
end

Expand All @@ -67,7 +77,6 @@ function Event(locations::Vector{<:AbstractCoordinatePoint{T}}, energies::Vector
max_interaction_distance::Union{<:Real, <:LengthQuantity} = NaN
)::Event{T} where {T <: SSDFloat, PT <: ParticleType}


@assert eachindex(locations) == eachindex(energies) == eachindex(radius)

d::T = T(to_internal_units(max_interaction_distance))
Expand Down Expand Up @@ -99,14 +108,16 @@ function Event(locations::Vector{<:AbstractCoordinatePoint{T}}, energies::Vector
end

function Event(
locations::Vector{<:Vector{<:AbstractCoordinatePoint{T}}},
energies::Vector{<:Vector{<:RealQuantity}}, N::Int;
particle_type::Type{PT} = Gamma, number_of_shells::Int = 2,
radius::Vector{<:Vector{<:RealQuantity}} = map(e -> radius_guess.(T.(to_internal_units.(e)), particle_type), energies)
) where {T <: SSDFloat, PT <: ParticleType}

@assert eachindex(locations) == eachindex(energies) == eachindex(radius)
events = map(i -> Event(locations[i], energies[i], N; particle_type, number_of_shells, radius = radius[i]), eachindex(locations))
locations::Vector{<:Vector{<:AbstractCoordinatePoint}},
energies::Vector{<:Vector{<:RealQuantity}}, N::Int;
particle_type::Type = Gamma, number_of_shells::Int = 2,
radius::Vector{<:Vector{<:RealQuantity}} = map(e -> radius_guess.(to_internal_units.(e), particle_type), energies)
)
locs_internal = [to_internal_units.(loc) for loc in locations]

@assert eachindex(locs_internal) == eachindex(energies) == eachindex(radius)
events = map(i -> Event(locs_internal[i], energies[i], N; particle_type, number_of_shells, radius = radius[i]), eachindex(locations))

Event(flatview.(getfield.(events, :locations)), flatview.(getfield.(events, :energies)))
end

Expand Down Expand Up @@ -375,4 +386,4 @@ function get_electron_and_hole_contribution(evt::Event{T}, sim::Simulation{T, S}
hole_contribution = RDWaveform(range(zero(T) * u"ns", step = dt * u"ns", length = length(signal_h)), signal_h * calibration_factor))
end

export get_electron_and_hole_contribution
export get_electron_and_hole_contribution
Loading
Loading