Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PhaseSpacePoint definition #51

Merged
merged 12 commits into from
May 7, 2024
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.1.0"

[deps]
QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
QEDbase = "0.1"
Expand Down
2 changes: 2 additions & 0 deletions src/QEDprocesses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export propagator
export AbstractCoordinateSystem, SphericalCoordinateSystem
export AbstractFrameOfReference, CenterOfMomentumFrame, ElectronRestFrame
export AbstractPhasespaceDefinition, PhasespaceDefinition
export ParticleStateful, PhaseSpacePoint
export spin, pol, nth_momentum, getindex

using QEDbase

Expand Down
182 changes: 179 additions & 3 deletions src/phase_spaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@
# TODO: ship to interfaces!
#####################

using StaticArrays

import QEDbase.is_particle
import QEDbase.is_anti_particle
import QEDbase.is_fermion
import QEDbase.is_boson
import QEDbase.is_incoming
import QEDbase.is_outgoing
import QEDbase.mass
import QEDbase.charge

import QEDbase:
is_particle,
is_anti_particle,
is_fermion,
is_boson,
is_incoming,
is_outgoing,
mass,
charge,
AbstractFourMomentum,
AbstractParticleType,
AbstractParticle

abstract type AbstractCoordinateSystem end
struct SphericalCoordinateSystem <: AbstractCoordinateSystem end

Expand All @@ -32,15 +56,15 @@ end
#
# Currently, elements can be either four-momenta, or real numbers,
# i.e. coordinates.
AbstractPhasespaceElement = Union{QEDbase.AbstractFourMomentum,Real}
AbstractPhasespaceElement = Union{AbstractFourMomentum,Real}

# utility functions

@inline function _check_in_phase_space_dimension(
proc::AbstractProcessDefinition,
::AbstractModelDefinition,
in_phase_space::AbstractVecOrMat{T},
) where {T<:QEDbase.AbstractFourMomentum}
) where {T<:AbstractFourMomentum}
return size(in_phase_space, 1) == number_incoming_particles(proc) || throw(
DimensionMismatch(
"The number of incoming particles <$(number_incoming_particles(proc))> is inconsistent with input size <$(size(in_phase_space,1))>",
Expand All @@ -52,7 +76,7 @@ end
proc::AbstractProcessDefinition,
::AbstractModelDefinition,
out_phase_space::AbstractVecOrMat{T},
) where {T<:QEDbase.AbstractFourMomentum}
) where {T<:AbstractFourMomentum}
return size(out_phase_space, 1) == number_outgoing_particles(proc) || throw(
DimensionMismatch(
"The number of outgoing particles <$(number_outgoing_particles(proc))> is inconsistent with input size <$(size(out_phase_space,1))>",
Expand Down Expand Up @@ -83,3 +107,155 @@ end
),
)
end

"""
ParticleStateful <: AbstractParticle

Representation of a particle with a state. It has four fields:
- `dir::ParticleDirection`: The direction of the particle, `QEDbase.Incoming()` or `QEDbase.Outgoing()`.
- `species::AbstractParticleType`: The species of the particle, `QEDbase.Electron()`, `QEDbase.Positron()` etc..
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved
- `mom::AbstractFourMomentum`: The momentum of the particle.
- `spin_or_pol::AbstractSpinOrPolarization`: The spin or polarization of the particle, `QEDbase.SpinUp()`, `QEDbase.PolX() etc.. Can only use spins with fermions and polarizations with bosons.
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved

Overloads for `QEDbase.is_fermion`, `QEDbase.is_boson`, `QEDbase.is_particle`, `QEDbase.is_anti_particle`, `QEDbase.is_incoming`, `QEDbase.is_outgoing`, `QEDbase.mass`, and `QEDbase.charge` are provided, delegating the call to the correct field and thus implementing the `QEDbase.AbstractParticle` interface.

The legality of the combination of `type` and `spinorpol` is checked on construction. If, for example, the construction of an `Electron()` with a polarization is attempted, an [`InvalidInputError`](@ref) is thrown.
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved
"""
struct ParticleStateful{ElType<:AbstractFourMomentum} <: AbstractParticle
dir::ParticleDirection
species::AbstractParticleType
mom::ElType
spin_or_pol::AbstractSpinOrPolarization

function ParticleStateful(
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved
dir::ParticleDirection, species::Species, mom::ElType, spin::Spin=AllSpin()
) where {Species<:FermionLike,ElType<:AbstractFourMomentum,Spin<:AbstractSpin}
# constructor for fermions with spin
return new{ElType}(dir, species, mom, spin)
end

function ParticleStateful(
dir::ParticleDirection, species::Species, mom::ElType, pol::Pol=AllPol()
) where {Species<:BosonLike,ElType<:AbstractFourMomentum,Pol<:AbstractPolarization}
# constructor for bosons with polarization
return new{ElType}(dir, species, mom, pol)
end
end

@inline is_incoming(particle::ParticleStateful) = is_incoming(particle.dir)
@inline is_outgoing(particle::ParticleStateful) = is_outgoing(particle.dir)
@inline is_fermion(particle::ParticleStateful) = is_fermion(particle.species)
@inline is_boson(particle::ParticleStateful) = is_boson(particle.species)
@inline is_particle(particle::ParticleStateful) = is_particle(particle.species)
@inline is_anti_particle(particle::ParticleStateful) = is_anti_particle(particle.species)
@inline mass(particle::ParticleStateful) = mass(particle.species)
@inline charge(particle::ParticleStateful) = charge(particle.species)

@inline _spin(::Species, particle::ParticleStateful) where {Species<:FermionLike} =
particle.spin_or_pol
@inline spin(particle::ParticleStateful) = _spin(particle.species, particle)

@inline _pol(::Species, particle::ParticleStateful) where {Species<:BosonLike} =
particle.spin_or_pol
@inline pol(particle::ParticleStateful) = _pol(particle.species, particle)
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved

"""
PhaseSpacePoint

Representation of a point in the phase space of a process. Contains the process ([`AbstractProcessDefinition`](@ref)), the model ([`AbstractModelDefinition`](@ref)), the phase space definition ([`AbstractPhasespaceDefinition`]), and stateful incoming and outgoing particles ([`ParticleStateful`](@ref)).

The legality of the combination of the given process and the incoming and outgoing particles is checked on construction. If the numbers of particles mismatch, the types of particles mismatch (note that order is important), or incoming particles have a `Outgoing` direction, an error is thrown.
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved
"""
struct PhaseSpacePoint{
PROC<:AbstractProcessDefinition,
MODEL<:AbstractModelDefinition,
PSDEF<:AbstractPhasespaceDefinition,
PhaseSpaceElementType<:AbstractFourMomentum,
N_IN_PARTICLES,
N_OUT_PARTICLES,
}
proc::PROC
model::MODEL
ps_def::PSDEF

in_particles::SVector{N_IN_PARTICLES,ParticleStateful{PhaseSpaceElementType}}
out_particles::SVector{N_OUT_PARTICLES,ParticleStateful{PhaseSpaceElementType}}

function PhaseSpacePoint(
proc::PROC, model::MODEL, ps_def::PSDEF, in_p::IN_P, out_p::OUT_P
) where {
PROC<:AbstractProcessDefinition,
MODEL<:AbstractModelDefinition,
PSDEF<:AbstractPhasespaceDefinition,
PhaseSpaceElementType<:AbstractFourMomentum,
IN_P<:AbstractVector{ParticleStateful{PhaseSpaceElementType}},
OUT_P<:AbstractVector{ParticleStateful{PhaseSpaceElementType}},
}
length(incoming_particles(proc)) == length(in_p) || throw(
InvalidInputError(
"The number of incoming particles given by the process ($(incoming_particles(proc))) mismatches the number of given stateful incoming particles ($(length(in_p)))",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)
length(outgoing_particles(proc)) == length(out_p) || throw(
InvalidInputError(
"The number of outgoing particles given by the process ($(outgoing_particles(proc))) mismatches the number of given stateful outgoing particles ($(length(out_p)))",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)

for (proc_p, p) in zip(incoming_particles(proc), in_p)
proc_p == p.species || throw(
InvalidInputError(
"Process given particle species ($(proc_p)) does not match stateful particle species ($(p.species))",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)
is_incoming(p) || throw(
InvalidInputError(
"Stateful particle $(p) is given as an incoming particle but is outgoing",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)
end
for (proc_p, p) in zip(outgoing_particles(proc), out_p)
proc_p == p.species || throw(
InvalidInputError(
"Process given particle species ($(proc_p)) does not match stateful particle species ($(p.species))",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)
is_outgoing(p) || throw(
InvalidInputError(
"Stateful particle $(p) is given as an outgoing particle but is incoming",
szabo137 marked this conversation as resolved.
Show resolved Hide resolved
),
)
end

return new{PROC,MODEL,PSDEF,PhaseSpaceElementType,length(in_p),length(out_p)}(
proc, model, ps_def, in_p, out_p
)
end
end

"""
Base.getindex(psp::PhaseSpacePoint, dir::Incoming, n::Int)

Overload for the array indexing operator `[]`. Returns the nth incoming particle in this phase space point.
"""
function Base.getindex(psp::PhaseSpacePoint, ::Incoming, n::Int)
return psp.in_particles[n]
end

"""
Base.getindex(psp::PhaseSpacePoint, dir::Outgoing, n::Int)

Overload for the array indexing operator `[]`. Returns the nth outgoing particle in this phase space point.
"""
function Base.getindex(psp::PhaseSpacePoint, ::Outgoing, n::Int)
return psp.out_particles[n]
end

"""
nth_momentum(psp::PhaseSpacePoint, dir::ParticleDirection, n::Int)
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved

Returns the momentum of the `n`th particle in the given [`PhaseSpacePoint`](@ref) which has direction `dir`. If `n` is outside the valid range for this phase space point, an `BoundsError` is thrown.
AntonReinhard marked this conversation as resolved.
Show resolved Hide resolved
"""
function nth_momentum(psp::PhaseSpacePoint, dir::ParticleDirection, n::Int)
return psp[dir, n].mom
end
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
133 changes: 133 additions & 0 deletions test/phase_spaces.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using Random
using StaticArrays
using QEDbase
using QEDprocesses

# can be removed when QEDbase exports them
import QEDbase.is_incoming, QEDbase.is_outgoing

include("test_implementation/TestImplementation.jl")
TESTMODEL = TestImplementation.TestModel()
TESTPSDEF = TestImplementation.TestPhasespaceDef()

RNG = Random.MersenneTwister(727)

@testset "Stateful Particle" begin
DIRECTIONS = [Incoming(), Outgoing()]
SPECIES = [Electron(), Positron()] #=, Muon(), AntiMuon(), Tauon(), AntiTauon()=#
SPINANDPOLS = [AllSpin(), SpinUp(), SpinDown(), AllPol(), PolX(), PolY()]

for (species, dir, spin_or_pol) in Iterators.product(SPECIES, DIRECTIONS, SPINANDPOLS)
momentum = rand(RNG, SFourMomentum)

if (is_fermion(species) && (spin_or_pol isa AbstractSpin)) ||
(is_boson(species) && (spin_or_pol isa AbstractPolarization))
particle_stateful = ParticleStateful(dir, species, momentum, spin_or_pol)

@test particle_stateful.mom == momentum
@test is_fermion(particle_stateful) == is_fermion(species)
@test is_boson(particle_stateful) == is_boson(species)
@test is_particle(particle_stateful) == is_particle(species)
@test is_anti_particle(particle_stateful) == is_anti_particle(species)
@test is_incoming(particle_stateful) == is_incoming(dir)
@test is_outgoing(particle_stateful) == is_outgoing(dir)
@test mass(particle_stateful) == mass(species)
@test charge(particle_stateful) == charge(species)

if (is_fermion(species))
@test spin(particle_stateful) == spin_or_pol
@test_throws MethodError pol(particle_stateful)
else
@test pol(particle_stateful) == spin_or_pol
@test_throws MethodError spin(particle_stateful)
end
else
if (VERSION >= v"1.8")
# julia versions before 1.8 did not have support for regex matching in @test_throws
@test_throws "MethodError: no method matching ParticleStateful" ParticleStateful(
dir, species, momentum, spin_or_pol
)
end
@test_throws MethodError ParticleStateful(dir, species, momentum, spin_or_pol)
end
end
end

@testset "Phasespace Point" begin
in_el = ParticleStateful(Incoming(), Electron(), rand(RNG, SFourMomentum))
in_ph = ParticleStateful(Incoming(), Photon(), rand(RNG, SFourMomentum))
out_el = ParticleStateful(Outgoing(), Electron(), rand(RNG, SFourMomentum))
out_ph = ParticleStateful(Outgoing(), Photon(), rand(RNG, SFourMomentum))

in_particles_valid = SVector(in_el, in_ph)
in_particles_invalid = SVector(in_el, out_ph)

out_particles_valid = SVector(out_el, out_ph)
out_particles_invalid = SVector(out_el, in_ph)

model = TESTMODEL
process = TestImplementation.TestProcess(
SVector{2,AbstractParticle}(Electron(), Photon()),
SVector{2,AbstractParticle}(Electron(), Photon()),
)
phasespace_def = TESTPSDEF

psp = PhaseSpacePoint(
process, model, phasespace_def, in_particles_valid, out_particles_valid
)

@test nth_momentum(psp, Incoming(), 1) == in_el.mom
@test nth_momentum(psp, Incoming(), 2) == in_ph.mom
@test nth_momentum(psp, Outgoing(), 1) == out_el.mom
@test nth_momentum(psp, Outgoing(), 2) == out_ph.mom

@test psp[Incoming(), 1] == in_el
@test psp[Incoming(), 2] == in_ph
@test psp[Outgoing(), 1] == out_el
@test psp[Outgoing(), 2] == out_ph

if (VERSION >= v"1.8")
# julia versions before 1.8 did not have support for regex matching in @test_throws
@test_throws r"Stateful particle (.*) is given as an incoming particle but is outgoing" PhaseSpacePoint(
process, model, phasespace_def, in_particles_invalid, out_particles_valid
)

@test_throws r"Stateful particle (.*) is given as an outgoing particle but is incoming" PhaseSpacePoint(
process, model, phasespace_def, in_particles_valid, out_particles_invalid
)

@test_throws r"Process given particle species \((.*)Electron\(\)\) does not match stateful particle species \((.*)Photon\(\)\)" PhaseSpacePoint(
process, model, phasespace_def, SVector(in_ph, in_el), out_particles_valid
)

@test_throws r"Process given particle species \((.*)Electron\(\)\) does not match stateful particle species \((.*)Photon\(\)\)" PhaseSpacePoint(
process, model, phasespace_def, in_particles_valid, SVector(out_ph, out_el)
)
end

@test_throws BoundsError nth_momentum(psp, Incoming(), -1)
@test_throws BoundsError nth_momentum(psp, Outgoing(), -1)
@test_throws BoundsError nth_momentum(psp, Incoming(), 4)
@test_throws BoundsError nth_momentum(psp, Outgoing(), 4)

@test_throws BoundsError psp[Incoming(), -1]
@test_throws BoundsError psp[Outgoing(), -1]
@test_throws BoundsError psp[Incoming(), 4]
@test_throws BoundsError psp[Outgoing(), 4]

@test_throws InvalidInputError PhaseSpacePoint(
process, model, phasespace_def, in_particles_invalid, out_particles_valid
)

@test_throws InvalidInputError PhaseSpacePoint(
process, model, phasespace_def, in_particles_valid, out_particles_invalid
)

@test_throws InvalidInputError PhaseSpacePoint(
process, model, phasespace_def, SVector(in_ph, in_el), out_particles_valid
)

@test_throws InvalidInputError PhaseSpacePoint(
process, model, phasespace_def, in_particles_valid, SVector(out_ph, out_el)
)
end
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ begin
@time @safetestset "cross section & probability" begin
include("cross_sections.jl")
end
@time @safetestset "phase spaces" begin
include("phase_spaces.jl")
end
end
Loading