_assert_particle_type_tuple(::Tuple{}) = nothing function _assert_particle_type_tuple(t::Tuple{AbstractParticleType,Vararg}) return _assert_particle_type_tuple(t[2:end]) end function _assert_particle_type_tuple(t::Any) throw( InvalidInputError( "invalid input, provide a tuple of AbstractParticleTypes to construct a GenericQEDProcess", ), ) end mutable struct GenericQEDProcess{INT,OUTT,INSP,OUTSP} <: AbstractProcessDefinition where { INT<:Tuple,OUTT<:Tuple,INSP<:Tuple,OUTSP<:Tuple } incoming_particles::INT outgoing_particles::OUTT incoming_spins_pols::INSP outgoing_spins_pols::OUTSP virtual_particles_cache::Vector function GenericQEDProcess( in_particles::INT, out_particles::OUTT, in_sp::INSP, out_sp::OUTSP ) where {INT<:Tuple,OUTT<:Tuple,INSP<:Tuple,OUTSP<:Tuple} _assert_particle_type_tuple(in_particles) _assert_particle_type_tuple(out_particles) return new{INT,OUTT,INSP,OUTSP}(in_particles, out_particles, in_sp, out_sp, []) end """ GenericQEDProcess(in_ph::Int, out_ph::Int, in_el::Int, out_el::Int, in_po::Int, out_po::Int) Convenience constructor from numbers of input/output photons, electrons and positrons. Uses `AllSpin()` and `AllPol()` for every particle's spin/pol by default. """ function GenericQEDProcess( in_ph::Int, out_ph::Int, in_el::Int, out_el::Int, in_po::Int, out_po::Int ) in_p = ntuple(i -> if i <= in_ph Photon() elseif i <= in_ph + in_el Electron() else Positron() end, in_ph + in_el + in_po) out_p = ntuple(i -> if i <= out_ph Photon() elseif i <= out_ph + out_el Electron() else Positron() end, out_ph + out_el + out_po) in_sp = tuple([i <= in_ph ? AllPol() : AllSpin() for i in 1:length(in_p)]...) out_sp = tuple([i <= out_ph ? AllPol() : AllSpin() for i in 1:length(out_p)]...) return GenericQEDProcess(in_p, out_p, in_sp, out_sp) end end function spin_or_pol( process::GenericQEDProcess, dir::ParticleDirection, species::AbstractParticleType, n::Int, ) i = 0 c = n for p in particles(process, dir) i += 1 if p == species c -= 1 end if c == 0 break end end if c != 0 || n <= 0 throw( InvalidInputError( "could not get $n-th spin/pol of $dir $species, does not exist" ), ) end if is_incoming(dir) return process.incoming_spins_pols[i] elseif is_outgoing(dir) return process.outgoing_spins_pols[i] else throw(InvalidInputError("unknown direction $(dir) given")) end end function QEDprocesses.incoming_particles(proc::GenericQEDProcess{INT,OUTT}) where {INT,OUTT} return proc.incoming_particles end function QEDprocesses.outgoing_particles(proc::GenericQEDProcess{INT,OUTT}) where {INT,OUTT} return proc.outgoing_particles end function isphysical(proc::GenericQEDProcess) return ( number_particles(proc, Incoming(), Electron()) + number_particles(proc, Outgoing(), Positron()) == number_particles(proc, Incoming(), Positron()) + number_particles(proc, Outgoing(), Electron()) ) && number_particles(proc, Incoming()) + number_particles(proc, Outgoing()) >= 2 end function matrix_element(proc::GenericQEDProcess, psp::PhaseSpacePoint) return nothing end