Add QED Model (#25)
Reviewed-on: Rubydragon/MetagraphOptimization.jl#25 Co-authored-by: Anton Reinhard <anton.reinhard@proton.me> Co-committed-by: Anton Reinhard <anton.reinhard@proton.me>
This commit is contained in:
@ -5,6 +5,8 @@ A module containing tools to work on DAGs.
|
||||
"""
|
||||
module MetagraphOptimization
|
||||
|
||||
using QEDbase
|
||||
|
||||
# graph types
|
||||
export DAG
|
||||
export Node
|
||||
@ -52,13 +54,25 @@ export get_operations
|
||||
# ABC model
|
||||
export ParticleValue
|
||||
export ParticleA, ParticleB, ParticleC
|
||||
export ABCProcessDescription, ABCProcessInput, ABCModel
|
||||
export ComputeTaskP
|
||||
export ComputeTaskS1
|
||||
export ComputeTaskS2
|
||||
export ComputeTaskV
|
||||
export ComputeTaskU
|
||||
export ComputeTaskSum
|
||||
export ABCParticle, ABCProcessDescription, ABCProcessInput, ABCModel
|
||||
export ComputeTaskABC_P
|
||||
export ComputeTaskABC_S1
|
||||
export ComputeTaskABC_S2
|
||||
export ComputeTaskABC_V
|
||||
export ComputeTaskABC_U
|
||||
export ComputeTaskABC_Sum
|
||||
|
||||
# QED model
|
||||
export FeynmanDiagram, FeynmanVertex, FeynmanTie, FeynmanParticle
|
||||
export PhotonStateful, FermionStateful, AntiFermionStateful
|
||||
export QEDParticle, QEDProcessDescription, QEDProcessInput, QEDModel
|
||||
export ComputeTaskQED_P
|
||||
export ComputeTaskQED_S1
|
||||
export ComputeTaskQED_S2
|
||||
export ComputeTaskQED_V
|
||||
export ComputeTaskQED_U
|
||||
export ComputeTaskQED_Sum
|
||||
export gen_graph
|
||||
|
||||
# code generation related
|
||||
export execute
|
||||
@ -83,6 +97,9 @@ export ==, in, show, isempty, delete!, length
|
||||
|
||||
export bytes_to_human_readable
|
||||
|
||||
# TODO: this is probably not good
|
||||
import QEDprocesses.compute
|
||||
|
||||
import Base.length
|
||||
import Base.show
|
||||
import Base.==
|
||||
@ -160,6 +177,15 @@ include("models/abc/properties.jl")
|
||||
include("models/abc/parse.jl")
|
||||
include("models/abc/print.jl")
|
||||
|
||||
include("models/qed/types.jl")
|
||||
include("models/qed/particle.jl")
|
||||
include("models/qed/diagrams.jl")
|
||||
include("models/qed/compute.jl")
|
||||
include("models/qed/create.jl")
|
||||
include("models/qed/properties.jl")
|
||||
include("models/qed/parse.jl")
|
||||
include("models/qed/print.jl")
|
||||
|
||||
include("devices/measure.jl")
|
||||
include("devices/detect.jl")
|
||||
include("devices/impl.jl")
|
||||
|
@ -61,13 +61,11 @@ function gen_input_assignment_code(
|
||||
|
||||
assignInputs = Vector{Expr}()
|
||||
for (name, symbols) in inputSymbols
|
||||
type = type_from_name(name)
|
||||
index = parse(Int, name[2:end])
|
||||
|
||||
(type, index) = type_index_from_name(model(processDescription), name)
|
||||
p = nothing
|
||||
|
||||
if (index > in_particles(processDescription)[type])
|
||||
index -= in_particles(processDescription)[type]
|
||||
if (index > get(in_particles(processDescription), type, 0))
|
||||
index -= get(in_particles(processDescription), type, 0)
|
||||
@assert index <= out_particles(processDescription)[type] "Too few particles of type $type in input particles for this process"
|
||||
|
||||
p = "filter(x -> typeof(x) <: $type, out_particles($(processInputSymbol)))[$(index)]"
|
||||
@ -76,10 +74,9 @@ function gen_input_assignment_code(
|
||||
end
|
||||
|
||||
for symbol in symbols
|
||||
# TODO: how to get the "default" cpu device?
|
||||
device = entry_device(machine)
|
||||
evalExpr = eval(gen_access_expr(device, symbol))
|
||||
push!(assignInputs, Meta.parse("$(evalExpr)::ParticleValue{$type} = ParticleValue($p, 1.0)"))
|
||||
push!(assignInputs, Meta.parse("$(evalExpr)::ParticleValue{$type} = ParticleValue($p, one(ComplexF64))"))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -123,7 +123,6 @@ function remove_edge!(graph::DAG, node1::Node, node2::Node; track = true, invali
|
||||
pre_length1 = length(node1.parents)
|
||||
pre_length2 = length(node2.children)
|
||||
|
||||
#TODO: filter is very slow
|
||||
for i in eachindex(node1.parents)
|
||||
if (node1.parents[i] == node2)
|
||||
splice!(node1.parents, i)
|
||||
@ -252,7 +251,6 @@ function invalidate_caches!(graph::DAG, operation::NodeFusion)
|
||||
delete!(graph.possibleOperations, operation)
|
||||
|
||||
# delete the operation from all caches of nodes involved in the operation
|
||||
# TODO: filter is very slow
|
||||
for n in [1, 3]
|
||||
for i in eachindex(operation.input[n].nodeFusions)
|
||||
if operation == operation.input[n].nodeFusions[i]
|
||||
|
@ -41,7 +41,7 @@ function show(io::IO, graph::DAG)
|
||||
if length(graph.nodes) <= 20
|
||||
show_nodes(io, graph)
|
||||
else
|
||||
print("Total: ", length(graph.nodes), ", ")
|
||||
print(io, "Total: ", length(graph.nodes), ", ")
|
||||
first = true
|
||||
i = 0
|
||||
for (type, number) in zip(keys(nodeDict), values(nodeDict))
|
||||
@ -49,12 +49,12 @@ function show(io::IO, graph::DAG)
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
print(", ")
|
||||
print(io, ", ")
|
||||
end
|
||||
if (i % 3 == 0)
|
||||
print("\n ")
|
||||
print(io, "\n ")
|
||||
end
|
||||
print(type, ": ", number)
|
||||
print(io, type, ": ", number)
|
||||
end
|
||||
end
|
||||
println(io)
|
||||
|
@ -1,46 +1,46 @@
|
||||
using AccurateArithmetic
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskP, data::ParticleValue)
|
||||
compute(::ComputeTaskABC_P, data::ABCParticleValue)
|
||||
|
||||
Return the particle and value as is.
|
||||
|
||||
0 FLOP.
|
||||
"""
|
||||
function compute(::ComputeTaskP, data::ParticleValue{P})::ParticleValue{P} where {P <: ABCParticle}
|
||||
function compute(::ComputeTaskABC_P, data::ABCParticleValue{P})::ABCParticleValue{P} where {P <: ABCParticle}
|
||||
return data
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskU, data::ParticleValue)
|
||||
compute(::ComputeTaskABC_U, data::ABCParticleValue)
|
||||
|
||||
Compute an outer edge. Return the particle value with the same particle and the value multiplied by an outer_edge factor.
|
||||
Compute an outer edge. Return the particle value with the same particle and the value multiplied by an ABC_outer_edge factor.
|
||||
|
||||
1 FLOP.
|
||||
"""
|
||||
function compute(::ComputeTaskU, data::ParticleValue{P})::ParticleValue{P} where {P <: ABCParticle}
|
||||
return ParticleValue(data.p, data.v * outer_edge(data.p))
|
||||
function compute(::ComputeTaskABC_U, data::ABCParticleValue{P})::ABCParticleValue{P} where {P <: ABCParticle}
|
||||
return ABCParticleValue{P}(data.p, data.v * ABC_outer_edge(data.p))
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskV, data1::ParticleValue, data2::ParticleValue)
|
||||
compute(::ComputeTaskABC_V, data1::ABCParticleValue, data2::ABCParticleValue)
|
||||
|
||||
Compute a vertex. Preserve momentum and particle types (AB->C etc.) to create resulting particle, multiply values together and times a vertex factor.
|
||||
|
||||
6 FLOP.
|
||||
"""
|
||||
function compute(
|
||||
::ComputeTaskV,
|
||||
data1::ParticleValue{P1},
|
||||
data2::ParticleValue{P2},
|
||||
)::ParticleValue where {P1 <: ABCParticle, P2 <: ABCParticle}
|
||||
p3 = preserve_momentum(data1.p, data2.p)
|
||||
dataOut = ParticleValue(p3, data1.v * vertex() * data2.v)
|
||||
::ComputeTaskABC_V,
|
||||
data1::ABCParticleValue{P1},
|
||||
data2::ABCParticleValue{P2},
|
||||
)::ABCParticleValue where {P1 <: ABCParticle, P2 <: ABCParticle}
|
||||
p3 = ABC_conserve_momentum(data1.p, data2.p)
|
||||
dataOut = ABCParticleValue{typeof(p3)}(p3, data1.v * ABC_vertex() * data2.v)
|
||||
return dataOut
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskS2, data1::ParticleValue, data2::ParticleValue)
|
||||
compute(::ComputeTaskABC_S2, data1::ABCParticleValue, data2::ABCParticleValue)
|
||||
|
||||
Compute a final inner edge (2 input particles, no output particle).
|
||||
|
||||
@ -48,112 +48,116 @@ For valid inputs, both input particles should have the same momenta at this poin
|
||||
|
||||
12 FLOP.
|
||||
"""
|
||||
function compute(::ComputeTaskS2, data1::ParticleValue{P}, data2::ParticleValue{P})::Float64 where {P <: ABCParticle}
|
||||
function compute(
|
||||
::ComputeTaskABC_S2,
|
||||
data1::ParticleValue{P},
|
||||
data2::ParticleValue{P},
|
||||
)::Float64 where {P <: ABCParticle}
|
||||
#=
|
||||
@assert isapprox(abs(data1.p.momentum.E), abs(data2.p.momentum.E), rtol = 0.001, atol = sqrt(eps())) "E: $(data1.p.momentum.E) vs. $(data2.p.momentum.E)"
|
||||
@assert isapprox(data1.p.momentum.px, -data2.p.momentum.px, rtol = 0.001, atol = sqrt(eps())) "px: $(data1.p.momentum.px) vs. $(data2.p.momentum.px)"
|
||||
@assert isapprox(data1.p.momentum.py, -data2.p.momentum.py, rtol = 0.001, atol = sqrt(eps())) "py: $(data1.p.momentum.py) vs. $(data2.p.momentum.py)"
|
||||
@assert isapprox(data1.p.momentum.pz, -data2.p.momentum.pz, rtol = 0.001, atol = sqrt(eps())) "pz: $(data1.p.momentum.pz) vs. $(data2.p.momentum.pz)"
|
||||
=#
|
||||
inner = inner_edge(data1.p)
|
||||
inner = ABC_inner_edge(data1.p)
|
||||
return data1.v * inner * data2.v
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskS1, data::ParticleValue)
|
||||
compute(::ComputeTaskABC_S1, data::ABCParticleValue)
|
||||
|
||||
Compute inner edge (1 input particle, 1 output particle).
|
||||
|
||||
11 FLOP.
|
||||
"""
|
||||
function compute(::ComputeTaskS1, data::ParticleValue{P})::ParticleValue{P} where {P <: ABCParticle}
|
||||
return ParticleValue(data.p, data.v * inner_edge(data.p))
|
||||
function compute(::ComputeTaskABC_S1, data::ABCParticleValue{P})::ABCParticleValue{P} where {P <: ABCParticle}
|
||||
return ABCParticleValue{P}(data.p, data.v * ABC_inner_edge(data.p))
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskSum, data::Vector{Float64})
|
||||
compute(::ComputeTaskABC_Sum, data::Vector{Float64})
|
||||
|
||||
Compute a sum over the vector. Use an algorithm that accounts for accumulated errors in long sums with potentially large differences in magnitude of the summands.
|
||||
|
||||
Linearly many FLOP with growing data.
|
||||
"""
|
||||
function compute(::ComputeTaskSum, data::Vector{Float64})::Float64
|
||||
function compute(::ComputeTaskABC_Sum, data::Vector{Float64})::Float64
|
||||
return sum_kbn(data)
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskP, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_P, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate and return code evaluating [`ComputeTaskP`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
Generate and return code evaluating [`ComputeTaskABC_P`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
"""
|
||||
function get_expression(::ComputeTaskP, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_P, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskP(), $(in[1]))")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_P(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskU, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_U, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskU`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`ParticleValue`](@ref), `outSym` will be of type [`ParticleValue`](@ref).
|
||||
Generate code evaluating [`ComputeTaskABC_U`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`ABCParticleValue`](@ref), `outSym` will be of type [`ABCParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskU, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_U, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskU(), $(in[1]))")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_U(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskV, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_V, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskV`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSym[1]` and `inSym[2]` should be of type [`ParticleValue`](@ref), `outSym` will be of type [`ParticleValue`](@ref).
|
||||
Generate code evaluating [`ComputeTaskABC_V`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSym[1]` and `inSym[2]` should be of type [`ABCParticleValue`](@ref), `outSym` will be of type [`ABCParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskV, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_V, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1]), eval(inExprs[2])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskV(), $(in[1]), $(in[2]))")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_V(), $(in[1]), $(in[2]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskS2, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_S2, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskS2`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms[1]` and `inSyms[2]` should be of type [`ParticleValue`](@ref), `outSym` will be of type `Float64`.
|
||||
Generate code evaluating [`ComputeTaskABC_S2`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms[1]` and `inSyms[2]` should be of type [`ABCParticleValue`](@ref), `outSym` will be of type `Float64`.
|
||||
"""
|
||||
function get_expression(::ComputeTaskS2, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_S2, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1]), eval(inExprs[2])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskS2(), $(in[1]), $(in[2]))")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_S2(), $(in[1]), $(in[2]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskS1, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_S1, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskS1`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`ParticleValue`](@ref), `outSym` will be of type [`ParticleValue`](@ref).
|
||||
Generate code evaluating [`ComputeTaskABC_S1`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`ABCParticleValue`](@ref), `outSym` will be of type [`ABCParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskS1, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_S1, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskS1(), $(in[1]))")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_S1(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskSum, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
get_expression(::ComputeTaskABC_Sum, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskSum`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
Generate code evaluating [`ComputeTaskABC_Sum`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`Float64`], `outSym` will be of type [`Float64`].
|
||||
"""
|
||||
function get_expression(::ComputeTaskSum, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
function get_expression(::ComputeTaskABC_Sum, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = eval.(inExprs)
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskSum(), [$(unroll_symbol_vector(in))])")
|
||||
return Meta.parse("$out = compute(ComputeTaskABC_Sum(), [$(unroll_symbol_vector(in))])")
|
||||
end
|
||||
|
@ -3,7 +3,7 @@ using Random
|
||||
using Roots
|
||||
using ForwardDiff
|
||||
|
||||
ComputeTaskSum() = ComputeTaskSum(0)
|
||||
ComputeTaskABC_Sum() = ComputeTaskABC_Sum(0)
|
||||
|
||||
"""
|
||||
gen_process_input(processDescription::ABCProcessDescription)
|
||||
@ -62,137 +62,3 @@ function gen_process_input(processDescription::ABCProcessDescription)
|
||||
|
||||
return return processInput
|
||||
end
|
||||
|
||||
####################
|
||||
# CODE FROM HERE BORROWED FROM SOURCE: https://codebase.helmholtz.cloud/qedsandbox/QEDphasespaces.jl/
|
||||
# use qedphasespaces directly once released
|
||||
#
|
||||
# quick and dirty implementation of the RAMBO algorithm
|
||||
#
|
||||
# reference:
|
||||
# * https://cds.cern.ch/record/164736/files/198601282.pdf
|
||||
# * https://www.sciencedirect.com/science/article/pii/0010465586901190
|
||||
####################
|
||||
|
||||
function generate_initial_moms(ss, masses)
|
||||
E1 = (ss^2 + masses[1]^2 - masses[2]^2) / (2 * ss)
|
||||
E2 = (ss^2 + masses[2]^2 - masses[1]^2) / (2 * ss)
|
||||
|
||||
rho1 = sqrt(E1^2 - masses[1]^2)
|
||||
rho2 = sqrt(E2^2 - masses[2]^2)
|
||||
|
||||
return [SFourMomentum(E1, 0, 0, rho1), SFourMomentum(E2, 0, 0, -rho2)]
|
||||
end
|
||||
|
||||
|
||||
Random.rand(rng::AbstractRNG, ::Random.SamplerType{SFourMomentum}) = SFourMomentum(rand(rng, 4))
|
||||
Random.rand(rng::AbstractRNG, ::Random.SamplerType{NTuple{N, Float64}}) where {N} = Tuple(rand(rng, N))
|
||||
|
||||
|
||||
function _transform_uni_to_mom(u1, u2, u3, u4)
|
||||
cth = 2 * u1 - 1
|
||||
sth = sqrt(1 - cth^2)
|
||||
phi = 2 * pi * u2
|
||||
q0 = -log(u3 * u4)
|
||||
qx = q0 * sth * cos(phi)
|
||||
qy = q0 * sth * sin(phi)
|
||||
qz = q0 * cth
|
||||
|
||||
return SFourMomentum(q0, qx, qy, qz)
|
||||
end
|
||||
|
||||
function _transform_uni_to_mom!(uni_mom, dest)
|
||||
u1, u2, u3, u4 = Tuple(uni_mom)
|
||||
cth = 2 * u1 - 1
|
||||
sth = sqrt(1 - cth^2)
|
||||
phi = 2 * pi * u2
|
||||
q0 = -log(u3 * u4)
|
||||
qx = q0 * sth * cos(phi)
|
||||
qy = q0 * sth * sin(phi)
|
||||
qz = q0 * cth
|
||||
|
||||
return dest = SFourMomentum(q0, qx, qy, qz)
|
||||
end
|
||||
|
||||
_transform_uni_to_mom(u1234::Tuple) = _transform_uni_to_mom(u1234...)
|
||||
_transform_uni_to_mom(u1234::SFourMomentum) = _transform_uni_to_mom(Tuple(u1234))
|
||||
|
||||
function generate_massless_moms(rng, n::Int)
|
||||
a = Vector{SFourMomentum}(undef, n)
|
||||
rand!(rng, a)
|
||||
return map(_transform_uni_to_mom, a)
|
||||
end
|
||||
|
||||
function generate_physical_massless_moms(rng, ss, n)
|
||||
r_moms = generate_massless_moms(rng, n)
|
||||
Q = sum(r_moms)
|
||||
M = sqrt(Q * Q)
|
||||
fac = -1 / M
|
||||
Qx = getX(Q)
|
||||
Qy = getY(Q)
|
||||
Qz = getZ(Q)
|
||||
bx = fac * Qx
|
||||
by = fac * Qy
|
||||
bz = fac * Qz
|
||||
gamma = getT(Q) / M
|
||||
a = 1 / (1 + gamma)
|
||||
x = ss / M
|
||||
|
||||
i = 1
|
||||
while i <= n
|
||||
mom = r_moms[i]
|
||||
mom0 = getT(mom)
|
||||
mom1 = getX(mom)
|
||||
mom2 = getY(mom)
|
||||
mom3 = getZ(mom)
|
||||
|
||||
bq = bx * mom1 + by * mom2 + bz * mom3
|
||||
|
||||
p0 = x * (gamma * mom0 + bq)
|
||||
px = x * (mom1 + bx * mom0 + a * bq * bx)
|
||||
py = x * (mom2 + by * mom0 + a * bq * by)
|
||||
pz = x * (mom3 + bz * mom0 + a * bq * bz)
|
||||
|
||||
r_moms[i] = SFourMomentum(p0, px, py, pz)
|
||||
i += 1
|
||||
end
|
||||
return r_moms
|
||||
end
|
||||
|
||||
function _to_be_solved(xi, masses, p0s, ss)
|
||||
sum = 0.0
|
||||
for (i, E) in enumerate(p0s)
|
||||
sum += sqrt(masses[i]^2 + xi^2 * E^2)
|
||||
end
|
||||
return sum - ss
|
||||
end
|
||||
|
||||
function _build_massive_momenta(xi, masses, massless_moms)
|
||||
vec = SFourMomentum[]
|
||||
i = 1
|
||||
while i <= length(massless_moms)
|
||||
massless_mom = massless_moms[i]
|
||||
k0 = sqrt(getT(massless_mom)^2 * xi^2 + masses[i]^2)
|
||||
|
||||
kx = xi * getX(massless_mom)
|
||||
ky = xi * getY(massless_mom)
|
||||
kz = xi * getZ(massless_mom)
|
||||
|
||||
push!(vec, SFourMomentum(k0, kx, ky, kz))
|
||||
|
||||
i += 1
|
||||
end
|
||||
return vec
|
||||
end
|
||||
|
||||
first_derivative(func) = x -> ForwardDiff.derivative(func, float(x))
|
||||
|
||||
|
||||
function generate_physical_massive_moms(rng, ss, masses; x0 = 0.1)
|
||||
n = length(masses)
|
||||
massless_moms = generate_physical_massless_moms(rng, ss, n)
|
||||
energies = getT.(massless_moms)
|
||||
f = x -> _to_be_solved(x, masses, energies, ss)
|
||||
xi = find_zero((f, first_derivative(f)), x0, Roots.Newton())
|
||||
return _build_massive_momenta(xi, masses, massless_moms)
|
||||
end
|
||||
|
@ -63,7 +63,7 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
end
|
||||
sizehint!(graph.nodes, estimate_no_nodes)
|
||||
|
||||
sum_node = insert_node!(graph, make_node(ComputeTaskSum(0)), track = false, invalidate_cache = false)
|
||||
sum_node = insert_node!(graph, make_node(ComputeTaskABC_Sum(0)), track = false, invalidate_cache = false)
|
||||
global_data_out = insert_node!(graph, make_node(DataTask(FLOAT_SIZE)), track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, sum_node, global_data_out, track = false, invalidate_cache = false)
|
||||
|
||||
@ -92,12 +92,12 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
track = false,
|
||||
invalidate_cache = false,
|
||||
) # read particle data node
|
||||
compute_P = insert_node!(graph, make_node(ComputeTaskP()), track = false, invalidate_cache = false) # compute P node
|
||||
compute_P = insert_node!(graph, make_node(ComputeTaskABC_P()), track = false, invalidate_cache = false) # compute P node
|
||||
data_Pu =
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false) # transfer data from P to u (one ParticleValue object)
|
||||
compute_u = insert_node!(graph, make_node(ComputeTaskU()), track = false, invalidate_cache = false) # compute U node
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false) # transfer data from P to u (one ABCParticleValue object)
|
||||
compute_u = insert_node!(graph, make_node(ComputeTaskABC_U()), track = false, invalidate_cache = false) # compute U node
|
||||
data_out =
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false) # transfer data out from u (one ParticleValue object)
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false) # transfer data out from u (one ABCParticleValue object)
|
||||
|
||||
insert_edge!(graph, data_in, compute_P, track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, compute_P, data_Pu, track = false, invalidate_cache = false)
|
||||
@ -112,13 +112,13 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
in1 = capt.captures[1]
|
||||
in2 = capt.captures[2]
|
||||
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskV()), track = false, invalidate_cache = false)
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskABC_V()), track = false, invalidate_cache = false)
|
||||
data_out =
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false)
|
||||
|
||||
if (occursin(regex_c, in1))
|
||||
# put an S node after this input
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskS1()), track = false, invalidate_cache = false)
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskABC_S1()), track = false, invalidate_cache = false)
|
||||
data_S_v = insert_node!(
|
||||
graph,
|
||||
make_node(DataTask(PARTICLE_VALUE_SIZE)),
|
||||
@ -137,7 +137,7 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
if (occursin(regex_c, in2))
|
||||
# i think the current generator only puts the combined particles in the first space, so this case might never be entered
|
||||
# put an S node after this input
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskS1()), track = false, invalidate_cache = false)
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskABC_S1()), track = false, invalidate_cache = false)
|
||||
data_S_v = insert_node!(
|
||||
graph,
|
||||
make_node(DataTask(PARTICLE_VALUE_SIZE)),
|
||||
@ -164,7 +164,7 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
in3 = capt.captures[3]
|
||||
|
||||
# in2 + in3 with a v
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskV()), track = false, invalidate_cache = false)
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskABC_V()), track = false, invalidate_cache = false)
|
||||
data_v =
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false)
|
||||
|
||||
@ -173,7 +173,7 @@ function parse_dag(filename::AbstractString, model::ABCModel, verbose::Bool = fa
|
||||
insert_edge!(graph, compute_v, data_v, track = false, invalidate_cache = false)
|
||||
|
||||
# combine with the v of the combined other input
|
||||
compute_S2 = insert_node!(graph, make_node(ComputeTaskS2()), track = false, invalidate_cache = false)
|
||||
compute_S2 = insert_node!(graph, make_node(ComputeTaskABC_S2()), track = false, invalidate_cache = false)
|
||||
data_out = insert_node!(graph, make_node(DataTask(FLOAT_SIZE)), track = false, invalidate_cache = false) # output of a S2 task is only a float
|
||||
|
||||
insert_edge!(graph, data_v, compute_S2, track = false, invalidate_cache = false)
|
||||
|
@ -1,5 +1,3 @@
|
||||
using QEDbase
|
||||
|
||||
import QEDbase.mass
|
||||
|
||||
"""
|
||||
@ -68,6 +66,8 @@ struct ABCProcessInput <: AbstractProcessInput
|
||||
outParticles::Vector{ABCParticle}
|
||||
end
|
||||
|
||||
ABCParticleValue{ParticleType <: ABCParticle} = ParticleValue{ParticleType, ComplexF64}
|
||||
|
||||
"""
|
||||
PARTICLE_MASSES
|
||||
|
||||
@ -119,65 +119,63 @@ function square(p::ABCParticle)
|
||||
end
|
||||
|
||||
"""
|
||||
inner_edge(p::ABCParticle)
|
||||
ABC_inner_edge(p::ABCParticle)
|
||||
|
||||
Return the factor of the inner edge with the given (virtual) particle.
|
||||
|
||||
Takes 10 effective FLOP. (3 here + 7 in square(p))
|
||||
"""
|
||||
function inner_edge(p::ABCParticle)
|
||||
function ABC_inner_edge(p::ABCParticle)
|
||||
return 1.0 / (square(p) - mass(typeof(p)) * mass(typeof(p)))
|
||||
end
|
||||
|
||||
"""
|
||||
outer_edge(p::ABCParticle)
|
||||
ABC_outer_edge(p::ABCParticle)
|
||||
|
||||
Return the factor of the outer edge with the given (real) particle.
|
||||
|
||||
Takes 0 effective FLOP.
|
||||
"""
|
||||
function outer_edge(p::ABCParticle)
|
||||
function ABC_outer_edge(p::ABCParticle)
|
||||
return 1.0
|
||||
end
|
||||
|
||||
"""
|
||||
vertex()
|
||||
ABC_vertex()
|
||||
|
||||
Return the factor of a vertex.
|
||||
|
||||
Takes 0 effective FLOP since it's constant.
|
||||
"""
|
||||
function vertex()
|
||||
function ABC_vertex()
|
||||
i = 1.0
|
||||
lambda = 1.0 / 137.0
|
||||
return i * lambda
|
||||
end
|
||||
|
||||
"""
|
||||
preserve_momentum(p1::ABCParticle, p2::ABCParticle)
|
||||
ABC_conserve_momentum(p1::ABCParticle, p2::ABCParticle)
|
||||
|
||||
Calculate and return a new particle from two given interacting ones at a vertex.
|
||||
|
||||
Takes 4 effective FLOP.
|
||||
"""
|
||||
function preserve_momentum(p1::ABCParticle, p2::ABCParticle)
|
||||
function ABC_conserve_momentum(p1::ABCParticle, p2::ABCParticle)
|
||||
t3 = interaction_result(typeof(p1), typeof(p2))
|
||||
p3 = t3(p1.momentum + p2.momentum)
|
||||
return p3
|
||||
end
|
||||
|
||||
"""
|
||||
type_from_name(name::String)
|
||||
model(::ABCProcessDescription) = ABCModel()
|
||||
model(::ABCProcessInput) = ABCModel()
|
||||
|
||||
For a name of a particle, return the particle's [`Type`].
|
||||
"""
|
||||
function type_from_name(name::String)
|
||||
function type_index_from_name(::ABCModel, name::String)
|
||||
if startswith(name, "A")
|
||||
return ParticleA
|
||||
return (ParticleA, parse(Int, name[2:end]))
|
||||
elseif startswith(name, "B")
|
||||
return ParticleB
|
||||
return (ParticleB, parse(Int, name[2:end]))
|
||||
elseif startswith(name, "C")
|
||||
return ParticleC
|
||||
return (ParticleC, parse(Int, name[2:end]))
|
||||
else
|
||||
throw("Invalid name for a particle in the ABC model")
|
||||
end
|
||||
|
@ -1,166 +1,134 @@
|
||||
"""
|
||||
compute_effort(t::ComputeTaskS1)
|
||||
compute_effort(t::ComputeTaskABC_S1)
|
||||
|
||||
Return the compute effort of an S1 task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskS1)::Float64 = 11.0
|
||||
compute_effort(t::ComputeTaskABC_S1)::Float64 = 11.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskS2)
|
||||
compute_effort(t::ComputeTaskABC_S2)
|
||||
|
||||
Return the compute effort of an S2 task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskS2)::Float64 = 12.0
|
||||
compute_effort(t::ComputeTaskABC_S2)::Float64 = 12.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskU)
|
||||
compute_effort(t::ComputeTaskABC_U)
|
||||
|
||||
Return the compute effort of a U task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskU)::Float64 = 1.0
|
||||
compute_effort(t::ComputeTaskABC_U)::Float64 = 1.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskV)
|
||||
compute_effort(t::ComputeTaskABC_V)
|
||||
|
||||
Return the compute effort of a V task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskV)::Float64 = 6.0
|
||||
compute_effort(t::ComputeTaskABC_V)::Float64 = 6.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskP)
|
||||
compute_effort(t::ComputeTaskABC_P)
|
||||
|
||||
Return the compute effort of a P task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskP)::Float64 = 0.0
|
||||
compute_effort(t::ComputeTaskABC_P)::Float64 = 0.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskSum)
|
||||
compute_effort(t::ComputeTaskABC_Sum)
|
||||
|
||||
Return the compute effort of a Sum task.
|
||||
|
||||
Note: This is a constant compute effort, even though sum scales with the number of its inputs. Since there is only ever a single sum node in a graph generated from the ABC-Model,
|
||||
this doesn't matter.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskSum)::Float64 = 1.0
|
||||
compute_effort(t::ComputeTaskABC_Sum)::Float64 = 1.0
|
||||
|
||||
"""
|
||||
show(io::IO, t::DataTask)
|
||||
|
||||
Print the data task to io.
|
||||
"""
|
||||
function show(io::IO, t::DataTask)
|
||||
return print(io, "Data", t.data)
|
||||
end
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskS1)
|
||||
show(io::IO, t::ComputeTaskABC_S1)
|
||||
|
||||
Print the S1 task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskS1) = print(io, "ComputeS1")
|
||||
show(io::IO, t::ComputeTaskABC_S1) = print(io, "ComputeS1")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskS2)
|
||||
show(io::IO, t::ComputeTaskABC_S2)
|
||||
|
||||
Print the S2 task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskS2) = print(io, "ComputeS2")
|
||||
show(io::IO, t::ComputeTaskABC_S2) = print(io, "ComputeS2")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskP)
|
||||
show(io::IO, t::ComputeTaskABC_P)
|
||||
|
||||
Print the P task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskP) = print(io, "ComputeP")
|
||||
show(io::IO, t::ComputeTaskABC_P) = print(io, "ComputeP")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskU)
|
||||
show(io::IO, t::ComputeTaskABC_U)
|
||||
|
||||
Print the U task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskU) = print(io, "ComputeU")
|
||||
show(io::IO, t::ComputeTaskABC_U) = print(io, "ComputeU")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskV)
|
||||
show(io::IO, t::ComputeTaskABC_V)
|
||||
|
||||
Print the V task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskV) = print(io, "ComputeV")
|
||||
show(io::IO, t::ComputeTaskABC_V) = print(io, "ComputeV")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskSum)
|
||||
show(io::IO, t::ComputeTaskABC_Sum)
|
||||
|
||||
Print the sum task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskSum) = print(io, "ComputeSum")
|
||||
show(io::IO, t::ComputeTaskABC_Sum) = print(io, "ComputeSum")
|
||||
|
||||
"""
|
||||
copy(t::DataTask)
|
||||
children(::ComputeTaskABC_S1)
|
||||
|
||||
Copy the data task and return it.
|
||||
Return the number of children of a ComputeTaskABC_S1 (always 1).
|
||||
"""
|
||||
copy(t::DataTask) = DataTask(t.data)
|
||||
children(::ComputeTaskABC_S1) = 1
|
||||
|
||||
"""
|
||||
children(::DataTask)
|
||||
children(::ComputeTaskABC_S2)
|
||||
|
||||
Return the number of children of a data task (always 1).
|
||||
Return the number of children of a ComputeTaskABC_S2 (always 2).
|
||||
"""
|
||||
children(::DataTask) = 1
|
||||
children(::ComputeTaskABC_S2) = 2
|
||||
|
||||
"""
|
||||
children(::ComputeTaskS1)
|
||||
children(::ComputeTaskABC_P)
|
||||
|
||||
Return the number of children of a ComputeTaskS1 (always 1).
|
||||
Return the number of children of a ComputeTaskABC_P (always 1).
|
||||
"""
|
||||
children(::ComputeTaskS1) = 1
|
||||
children(::ComputeTaskABC_P) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskS2)
|
||||
children(::ComputeTaskABC_U)
|
||||
|
||||
Return the number of children of a ComputeTaskS2 (always 2).
|
||||
Return the number of children of a ComputeTaskABC_U (always 1).
|
||||
"""
|
||||
children(::ComputeTaskS2) = 2
|
||||
children(::ComputeTaskABC_U) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskP)
|
||||
children(::ComputeTaskABC_V)
|
||||
|
||||
Return the number of children of a ComputeTaskP (always 1).
|
||||
Return the number of children of a ComputeTaskABC_V (always 2).
|
||||
"""
|
||||
children(::ComputeTaskP) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskU)
|
||||
|
||||
Return the number of children of a ComputeTaskU (always 1).
|
||||
"""
|
||||
children(::ComputeTaskU) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskV)
|
||||
|
||||
Return the number of children of a ComputeTaskV (always 2).
|
||||
"""
|
||||
children(::ComputeTaskV) = 2
|
||||
children(::ComputeTaskABC_V) = 2
|
||||
|
||||
|
||||
"""
|
||||
children(::ComputeTaskSum)
|
||||
children(::ComputeTaskABC_Sum)
|
||||
|
||||
Return the number of children of a ComputeTaskSum.
|
||||
Return the number of children of a ComputeTaskABC_Sum.
|
||||
"""
|
||||
children(t::ComputeTaskSum) = t.children_number
|
||||
children(t::ComputeTaskABC_Sum) = t.children_number
|
||||
|
||||
"""
|
||||
children(t::FusedComputeTask)
|
||||
|
||||
Return the number of children of a FusedComputeTask.
|
||||
"""
|
||||
function children(t::FusedComputeTask)
|
||||
return length(union(Set(t.t1_inputs), Set(t.t2_inputs)))
|
||||
end
|
||||
|
||||
function add_child!(t::ComputeTaskSum)
|
||||
function add_child!(t::ComputeTaskABC_Sum)
|
||||
t.children_number += 1
|
||||
return nothing
|
||||
end
|
||||
|
@ -1,53 +1,44 @@
|
||||
"""
|
||||
DataTask <: AbstractDataTask
|
||||
|
||||
Task representing a specific data transfer in the ABC Model.
|
||||
"""
|
||||
struct DataTask <: AbstractDataTask
|
||||
data::Float64
|
||||
end
|
||||
|
||||
"""
|
||||
ComputeTaskS1 <: AbstractComputeTask
|
||||
ComputeTaskABC_S1 <: AbstractComputeTask
|
||||
|
||||
S task with a single child.
|
||||
"""
|
||||
struct ComputeTaskS1 <: AbstractComputeTask end
|
||||
struct ComputeTaskABC_S1 <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskS2 <: AbstractComputeTask
|
||||
ComputeTaskABC_S2 <: AbstractComputeTask
|
||||
|
||||
S task with two children.
|
||||
"""
|
||||
struct ComputeTaskS2 <: AbstractComputeTask end
|
||||
struct ComputeTaskABC_S2 <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskP <: AbstractComputeTask
|
||||
ComputeTaskABC_P <: AbstractComputeTask
|
||||
|
||||
P task with no children.
|
||||
"""
|
||||
struct ComputeTaskP <: AbstractComputeTask end
|
||||
struct ComputeTaskABC_P <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskV <: AbstractComputeTask
|
||||
ComputeTaskABC_V <: AbstractComputeTask
|
||||
|
||||
v task with two children.
|
||||
"""
|
||||
struct ComputeTaskV <: AbstractComputeTask end
|
||||
struct ComputeTaskABC_V <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskU <: AbstractComputeTask
|
||||
ComputeTaskABC_U <: AbstractComputeTask
|
||||
|
||||
u task with a single child.
|
||||
"""
|
||||
struct ComputeTaskU <: AbstractComputeTask end
|
||||
struct ComputeTaskABC_U <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskSum <: AbstractComputeTask
|
||||
ComputeTaskABC_Sum <: AbstractComputeTask
|
||||
|
||||
Task that sums all its inputs, n children.
|
||||
"""
|
||||
mutable struct ComputeTaskSum <: AbstractComputeTask
|
||||
mutable struct ComputeTaskABC_Sum <: AbstractComputeTask
|
||||
children_number::Int
|
||||
end
|
||||
|
||||
@ -56,4 +47,5 @@ end
|
||||
|
||||
Constant vector of all tasks of the ABC-Model.
|
||||
"""
|
||||
ABC_TASKS = [DataTask, ComputeTaskS1, ComputeTaskS2, ComputeTaskP, ComputeTaskV, ComputeTaskU, ComputeTaskSum]
|
||||
ABC_TASKS =
|
||||
[ComputeTaskABC_S1, ComputeTaskABC_S2, ComputeTaskABC_P, ComputeTaskABC_V, ComputeTaskABC_U, ComputeTaskABC_Sum]
|
||||
|
@ -1,3 +1,5 @@
|
||||
import QEDbase.mass
|
||||
import QEDbase.AbstractParticle
|
||||
|
||||
"""
|
||||
AbstractPhysicsModel
|
||||
@ -6,23 +8,16 @@ Base type for a model, e.g. ABC-Model or QED. This is used to dispatch many func
|
||||
"""
|
||||
abstract type AbstractPhysicsModel end
|
||||
|
||||
"""
|
||||
AbstractParticle
|
||||
|
||||
Base type for particles belonging to a certain [`AbstractPhysicsModel`](@ref).
|
||||
"""
|
||||
abstract type AbstractParticle end
|
||||
|
||||
"""
|
||||
ParticleValue{ParticleType <: AbstractParticle}
|
||||
|
||||
A struct describing a particle during a calculation of a Feynman Diagram, together with the value that's being calculated.
|
||||
A struct describing a particle during a calculation of a Feynman Diagram, together with the value that's being calculated. `AbstractParticle` is the type from the QEDbase package.
|
||||
|
||||
`sizeof(ParticleValue())` = 48 Byte
|
||||
"""
|
||||
struct ParticleValue{ParticleType <: AbstractParticle}
|
||||
struct ParticleValue{ParticleType <: AbstractParticle, ValueType}
|
||||
p::ParticleType
|
||||
v::Float64
|
||||
v::ValueType
|
||||
end
|
||||
|
||||
"""
|
||||
@ -43,13 +38,6 @@ See also: [`gen_process_input`](@ref)
|
||||
"""
|
||||
abstract type AbstractProcessInput end
|
||||
|
||||
"""
|
||||
mass(t::Type{T}) where {T <: AbstractParticle}
|
||||
|
||||
Interface function that must be implemented for every subtype of [`AbstractParticle`](@ref), returning the particles mass at rest.
|
||||
"""
|
||||
function mass end
|
||||
|
||||
"""
|
||||
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: AbstractParticle, T2 <: AbstractParticle}
|
||||
|
||||
@ -107,3 +95,18 @@ Interface function that must be implemented for every specific [`AbstractProcess
|
||||
Returns a randomly generated and valid corresponding `ProcessInput`.
|
||||
"""
|
||||
function gen_process_input end
|
||||
|
||||
"""
|
||||
model(::AbstractProcessDescription)
|
||||
model(::AbstarctProcessInput)
|
||||
|
||||
Return the model of this process description or input.
|
||||
"""
|
||||
function model end
|
||||
|
||||
"""
|
||||
type_from_name(model::AbstractModel, name::String)
|
||||
|
||||
For a name of a particle in the given [`AbstractModel`](@ref), return the particle's [`Type`] and index as a tuple. The input string can be expetced to be of the form \"<name><index>\".
|
||||
"""
|
||||
function type_index_from_name end
|
||||
|
198
src/models/qed/compute.jl
Normal file
198
src/models/qed/compute.jl
Normal file
@ -0,0 +1,198 @@
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_P, data::QEDParticleValue)
|
||||
|
||||
Return the particle as is and initialize the Value.
|
||||
"""
|
||||
function compute(::ComputeTaskQED_P, data::QEDParticleValue{P})::QEDParticleValue{P} where {P <: QEDParticle}
|
||||
# TODO do we actually need this for anything?
|
||||
return QEDParticleValue{P}(data.p, one(DiracMatrix))
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_U, data::QEDParticleValue)
|
||||
|
||||
Compute an outer edge. Return the particle value with the same particle and the value multiplied by an outer_edge factor.
|
||||
"""
|
||||
function compute(::ComputeTaskQED_U, data::PV) where {P <: QEDParticle, PV <: QEDParticleValue{P}}
|
||||
state = base_state(particle(data.p), direction(data.p), momentum(data.p), spin_or_pol(data.p))
|
||||
return ParticleValue{P, typeof(state)}(
|
||||
data.p,
|
||||
state, # will return a SLorentzVector{ComplexF64}, BiSpinor or AdjointBiSpinor
|
||||
)
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_V, data1::QEDParticleValue, data2::QEDParticleValue)
|
||||
|
||||
Compute a vertex. Preserve momentum and particle types (e + gamma->p etc.) to create resulting particle, multiply values together and times a vertex factor.
|
||||
"""
|
||||
function compute(
|
||||
::ComputeTaskQED_V,
|
||||
data1::PV1,
|
||||
data2::PV2,
|
||||
) where {P1 <: QEDParticle, P2 <: QEDParticle, PV1 <: QEDParticleValue{P1}, PV2 <: QEDParticleValue{P2}}
|
||||
p3 = QED_conserve_momentum(data1.p, data2.p)
|
||||
P3 = interaction_result(P1, P2)
|
||||
|
||||
state = QED_vertex()
|
||||
if (typeof(data1.v) <: AdjointBiSpinor)
|
||||
state = data1.v * state
|
||||
else
|
||||
state = state * data1.v
|
||||
end
|
||||
if (typeof(data2.v) <: AdjointBiSpinor)
|
||||
state = data2.v * state
|
||||
else
|
||||
state = state * data2.v
|
||||
end
|
||||
|
||||
dataOut = ParticleValue{P3, typeof(state)}(P3(p3), state)
|
||||
return dataOut
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_S2, data1::QEDParticleValue, data2::QEDParticleValue)
|
||||
|
||||
Compute a final inner edge (2 input particles, no output particle).
|
||||
|
||||
For valid inputs, both input particles should have the same momenta at this point.
|
||||
|
||||
12 FLOP.
|
||||
"""
|
||||
function compute(
|
||||
::ComputeTaskQED_S2,
|
||||
data1::ParticleValue{P1},
|
||||
data2::ParticleValue{P2},
|
||||
)::ComplexF64 where {
|
||||
P1 <: Union{AntiFermionStateful, FermionStateful},
|
||||
P2 <: Union{AntiFermionStateful, FermionStateful},
|
||||
}
|
||||
@assert isapprox(data1.p.momentum, data2.p.momentum, rtol = sqrt(eps()), atol = sqrt(eps())) "$(data1.p.momentum) vs. $(data2.p.momentum)"
|
||||
|
||||
inner = QED_inner_edge(propagation_result(P1)(data1.p))
|
||||
|
||||
# inner edge is just a "scalar", data1 and data2 are bispinor/adjointbispinnor, need to keep correct order
|
||||
if typeof(data1.v) <: BiSpinor
|
||||
return data2.v * inner * data1.v
|
||||
else
|
||||
return data1.v * inner * data2.v
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: S2 when the particles are photons?
|
||||
function compute(
|
||||
::ComputeTaskQED_S2,
|
||||
data1::ParticleValue{P1},
|
||||
data2::ParticleValue{P2},
|
||||
)::ComplexF64 where {P1 <: PhotonStateful, P2 <: PhotonStateful}
|
||||
# TODO: assert that data1 and data2 are opposites
|
||||
inner = QED_inner_edge(data1.p)
|
||||
# inner edge is just a scalar, data1 and data2 are photon states that are just Complex numbers here
|
||||
return data1.v * inner * data2.v
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_S1, data::QEDParticleValue)
|
||||
|
||||
Compute inner edge (1 input particle, 1 output particle).
|
||||
"""
|
||||
function compute(::ComputeTaskQED_S1, data::QEDParticleValue{P})::QEDParticleValue where {P <: QEDParticle}
|
||||
newP = propagation_result(P)
|
||||
new_p = newP(data.p)
|
||||
# inner edge is just a scalar, can multiply from either side
|
||||
if typeof(data.v) <: BiSpinor
|
||||
return ParticleValue(new_p, QED_inner_edge(new_p) * data.v)
|
||||
else
|
||||
return ParticleValue(new_p, data.v * QED_inner_edge(new_p))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
compute(::ComputeTaskQED_Sum, data::Vector{ComplexF64})
|
||||
|
||||
Compute a sum over the vector. Use an algorithm that accounts for accumulated errors in long sums with potentially large differences in magnitude of the summands.
|
||||
|
||||
Linearly many FLOP with growing data.
|
||||
"""
|
||||
function compute(::ComputeTaskQED_Sum, data::Vector{ComplexF64})::ComplexF64
|
||||
# TODO: want to use sum_kbn here but it doesn't seem to support ComplexF64, do it element-wise?
|
||||
return sum(data)
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_P, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate and return code evaluating [`ComputeTaskQED_P`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_P, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_P(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_U, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskQED_U`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`QEDParticleValue`](@ref), `outSym` will be of type [`QEDParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_U, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_U(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_V, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskQED_V`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSym[1]` and `inSym[2]` should be of type [`QEDParticleValue`](@ref), `outSym` will be of type [`QEDParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_V, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1]), eval(inExprs[2])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_V(), $(in[1]), $(in[2]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_S2, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskQED_S2`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms[1]` and `inSyms[2]` should be of type [`QEDParticleValue`](@ref), `outSym` will be of type `Float64`.
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_S2, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1]), eval(inExprs[2])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_S2(), $(in[1]), $(in[2]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_S1, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskQED_S1`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`QEDParticleValue`](@ref), `outSym` will be of type [`QEDParticleValue`](@ref).
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_S1, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = [eval(inExprs[1])]
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_S1(), $(in[1]))")
|
||||
end
|
||||
|
||||
"""
|
||||
get_expression(::ComputeTaskQED_Sum, device::AbstractDevice, inExprs::Vector{Expr}, outExpr::Expr)
|
||||
|
||||
Generate code evaluating [`ComputeTaskQED_Sum`](@ref) on `inSyms`, providing the output on `outSym`.
|
||||
`inSyms` should be of type [`Float64`], `outSym` will be of type [`Float64`].
|
||||
"""
|
||||
function get_expression(::ComputeTaskQED_Sum, device::AbstractDevice, inExprs::Vector, outExpr)
|
||||
in = eval.(inExprs)
|
||||
out = eval(outExpr)
|
||||
|
||||
return Meta.parse("$out = compute(ComputeTaskQED_Sum(), [$(unroll_symbol_vector(in))])")
|
||||
end
|
172
src/models/qed/create.jl
Normal file
172
src/models/qed/create.jl
Normal file
@ -0,0 +1,172 @@
|
||||
|
||||
ComputeTaskQED_Sum() = ComputeTaskQED_Sum(0)
|
||||
|
||||
"""
|
||||
gen_process_input(processDescription::QEDProcessDescription)
|
||||
|
||||
Return a ProcessInput of randomly generated [`QEDParticle`](@ref)s from a [`QEDProcessDescription`](@ref). The process description can be created manually or parsed from a string using [`parse_process`](@ref).
|
||||
|
||||
Note: This uses RAMBO to create a valid process with conservation of momentum and energy.
|
||||
"""
|
||||
function gen_process_input(processDescription::QEDProcessDescription)
|
||||
massSum = 0
|
||||
inputMasses = Vector{Float64}()
|
||||
for (particle, n) in processDescription.inParticles
|
||||
for _ in 1:n
|
||||
massSum += mass(particle)
|
||||
push!(inputMasses, mass(particle))
|
||||
end
|
||||
end
|
||||
outputMasses = Vector{Float64}()
|
||||
for (particle, n) in processDescription.outParticles
|
||||
for _ in 1:n
|
||||
massSum += mass(particle)
|
||||
push!(outputMasses, mass(particle))
|
||||
end
|
||||
end
|
||||
|
||||
# add some extra random mass to allow for some momentum
|
||||
massSum += rand(rng[threadid()]) * (length(inputMasses) + length(outputMasses))
|
||||
|
||||
|
||||
inputParticles = Vector{QEDParticle}()
|
||||
initialMomenta = generate_initial_moms(massSum, inputMasses)
|
||||
index = 1
|
||||
for (particle, n) in processDescription.inParticles
|
||||
for _ in 1:n
|
||||
mom = initialMomenta[index]
|
||||
push!(inputParticles, particle(mom))
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
|
||||
outputParticles = Vector{QEDParticle}()
|
||||
final_momenta = generate_physical_massive_moms(rng[threadid()], massSum, outputMasses)
|
||||
index = 1
|
||||
for (particle, n) in processDescription.outParticles
|
||||
for _ in 1:n
|
||||
push!(outputParticles, particle(final_momenta[index]))
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
|
||||
processInput = QEDProcessInput(processDescription, inputParticles, outputParticles)
|
||||
|
||||
return return processInput
|
||||
end
|
||||
|
||||
"""
|
||||
gen_graph(process_description::QEDProcessDescription)
|
||||
|
||||
For a given [`QEDProcessDescription`](@ref), return the [`DAG`](@ref) that computes it.
|
||||
"""
|
||||
function gen_graph(process_description::QEDProcessDescription)
|
||||
initial_diagram = FeynmanDiagram(process_description)
|
||||
diagrams = gen_diagrams(initial_diagram)
|
||||
|
||||
graph = DAG()
|
||||
|
||||
COMPLEX_SIZE = sizeof(ComplexF64)
|
||||
PARTICLE_VALUE_SIZE = 96.0
|
||||
|
||||
# TODO: Not all diagram outputs should always be summed at the end, if they differ by fermion exchange they need to be diffed
|
||||
# Should not matter for n-Photon Compton processes though
|
||||
sum_node = insert_node!(graph, make_node(ComputeTaskQED_Sum(0)), track = false, invalidate_cache = false)
|
||||
global_data_out = insert_node!(graph, make_node(DataTask(COMPLEX_SIZE)), track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, sum_node, global_data_out, track = false, invalidate_cache = false)
|
||||
|
||||
# remember the data out nodes for connection
|
||||
dataOutNodes = Dict()
|
||||
|
||||
for particle in initial_diagram.particles
|
||||
# generate data in and U tasks
|
||||
data_in = insert_node!(
|
||||
graph,
|
||||
make_node(DataTask(PARTICLE_VALUE_SIZE), String(particle)),
|
||||
track = false,
|
||||
invalidate_cache = false,
|
||||
) # read particle data node
|
||||
compute_u = insert_node!(graph, make_node(ComputeTaskQED_U()), track = false, invalidate_cache = false) # compute U node
|
||||
data_out =
|
||||
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false) # transfer data out from u (one ABCParticleValue object)
|
||||
|
||||
insert_edge!(graph, data_in, compute_u, track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, compute_u, data_out, track = false, invalidate_cache = false)
|
||||
|
||||
# remember the data_out node for future edges
|
||||
dataOutNodes[String(particle)] = data_out
|
||||
end
|
||||
|
||||
#dataOutBackup = copy(dataOutNodes)
|
||||
|
||||
for diagram in diagrams
|
||||
# the intermediate (virtual) particles change across
|
||||
#dataOutNodes = copy(dataOutBackup)
|
||||
|
||||
tie = diagram.tie[]
|
||||
|
||||
# handle the vertices
|
||||
for vertices in diagram.vertices
|
||||
for vertex in vertices
|
||||
data_in1 = dataOutNodes[String(vertex.in1)]
|
||||
data_in2 = dataOutNodes[String(vertex.in2)]
|
||||
|
||||
compute_V = insert_node!(graph, make_node(ComputeTaskQED_V()), track = false, invalidate_cache = false) # compute vertex
|
||||
|
||||
insert_edge!(graph, data_in1, compute_V, track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, data_in2, compute_V, track = false, invalidate_cache = false)
|
||||
|
||||
data_V_out = insert_node!(
|
||||
graph,
|
||||
make_node(DataTask(PARTICLE_VALUE_SIZE)),
|
||||
track = false,
|
||||
invalidate_cache = false,
|
||||
)
|
||||
|
||||
insert_edge!(graph, compute_V, data_V_out, track = false, invalidate_cache = false)
|
||||
|
||||
if (vertex.out == tie.in1 || vertex.out == tie.in2)
|
||||
# out particle is part of the tie -> there will be an S2 task with it later, don't make S1 task
|
||||
dataOutNodes[String(vertex.out)] = data_V_out
|
||||
continue
|
||||
end
|
||||
|
||||
# otherwise, add S1 task
|
||||
compute_S1 =
|
||||
insert_node!(graph, make_node(ComputeTaskQED_S1()), track = false, invalidate_cache = false) # compute propagator
|
||||
|
||||
insert_edge!(graph, data_V_out, compute_S1, track = false, invalidate_cache = false)
|
||||
|
||||
data_S1_out = insert_node!(
|
||||
graph,
|
||||
make_node(DataTask(PARTICLE_VALUE_SIZE)),
|
||||
track = false,
|
||||
invalidate_cache = false,
|
||||
)
|
||||
|
||||
insert_edge!(graph, compute_S1, data_S1_out, track = false, invalidate_cache = false)
|
||||
|
||||
# overrides potentially different nodes from previous diagrams, which is intentional
|
||||
dataOutNodes[String(vertex.out)] = data_S1_out
|
||||
end
|
||||
end
|
||||
|
||||
# handle the tie
|
||||
data_in1 = dataOutNodes[String(tie.in1)]
|
||||
data_in2 = dataOutNodes[String(tie.in2)]
|
||||
|
||||
compute_S2 = insert_node!(graph, make_node(ComputeTaskQED_S2()), track = false, invalidate_cache = false)
|
||||
|
||||
data_S2 = insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)), track = false, invalidate_cache = false)
|
||||
|
||||
insert_edge!(graph, data_in1, compute_S2, track = false, invalidate_cache = false)
|
||||
insert_edge!(graph, data_in2, compute_S2, track = false, invalidate_cache = false)
|
||||
|
||||
insert_edge!(graph, compute_S2, data_S2, track = false, invalidate_cache = false)
|
||||
|
||||
insert_edge!(graph, data_S2, sum_node, track = false, invalidate_cache = false)
|
||||
add_child!(task(sum_node))
|
||||
end
|
||||
|
||||
return graph
|
||||
end
|
484
src/models/qed/diagrams.jl
Normal file
484
src/models/qed/diagrams.jl
Normal file
@ -0,0 +1,484 @@
|
||||
|
||||
import Base.copy
|
||||
import Base.hash
|
||||
import Base.==
|
||||
import Base.show
|
||||
|
||||
"""
|
||||
FeynmanParticle
|
||||
|
||||
Representation of a particle for use in [`FeynmanDiagram`](@ref)s. Consist of the [`QEDParticle`](@ref) type and an id.
|
||||
"""
|
||||
struct FeynmanParticle
|
||||
particle::Type{<:QEDParticle}
|
||||
id::Int
|
||||
end
|
||||
|
||||
"""
|
||||
FeynmanVertex
|
||||
|
||||
Representation of a vertex in a [`FeynmanDiagram`](@ref). Stores two input [`FeynmanParticle`](@ref)s and one output.
|
||||
"""
|
||||
struct FeynmanVertex
|
||||
in1::FeynmanParticle
|
||||
in2::FeynmanParticle
|
||||
out::FeynmanParticle
|
||||
end
|
||||
|
||||
"""
|
||||
FeynmanTie
|
||||
|
||||
Representation of a "tie" in a [`FeynmanDiagram`](@ref). A tie ties two virtual particles in a diagram together and thus represent an inner line of the diagram. Not all inner lines are [`FeynmanTie`](@ref)s, in fact, a connected diagram only ever has exactly one tie.
|
||||
"""
|
||||
struct FeynmanTie
|
||||
in1::FeynmanParticle
|
||||
in2::FeynmanParticle
|
||||
end
|
||||
|
||||
"""
|
||||
FeynmanDiagram
|
||||
|
||||
Representation of a feynman diagram. It consists of its initial input/output particles, and a vector of sets of [`FeynmanVertex`](@ref)s. The vertices are to be applied level by level.
|
||||
A [`FeynmanVertex`](@ref) will always be at the lowest level possible, i.e. the lowest level at which all input particles for it exist.
|
||||
The [`FeynmanTie`](@ref) represents the final inner edge of the diagram.
|
||||
"""
|
||||
struct FeynmanDiagram
|
||||
vertices::Vector{Set{FeynmanVertex}}
|
||||
tie::Ref{Union{FeynmanTie, Missing}}
|
||||
particles::Vector{FeynmanParticle}
|
||||
type_ids::Dict{Type, Int64} # lut for number of used ids for a particle type
|
||||
end
|
||||
|
||||
"""
|
||||
FeynmanDiagram(pd::QEDProcessDescription)
|
||||
|
||||
Create an initial [`FeynmanDiagram`](@ref) with only its initial particles set and no vertices or ties.
|
||||
|
||||
Use [`gen_diagrams`](@ref) to generate all possible diagrams from this one.
|
||||
"""
|
||||
function FeynmanDiagram(pd::QEDProcessDescription)
|
||||
parts = Vector{FeynmanParticle}()
|
||||
for (type, n) in pd.inParticles
|
||||
for i in 1:n
|
||||
push!(parts, FeynmanParticle(type, i))
|
||||
end
|
||||
end
|
||||
for (type, n) in pd.outParticles
|
||||
for i in 1:n
|
||||
push!(parts, FeynmanParticle(type, i))
|
||||
end
|
||||
end
|
||||
ids = Dict{Type, Int64}()
|
||||
for t in types(QEDModel())
|
||||
if (isincoming(t))
|
||||
ids[t] = get(pd.inParticles, t, 0)
|
||||
else
|
||||
ids[t] = get(pd.outParticles, t, 0)
|
||||
end
|
||||
end
|
||||
|
||||
return FeynmanDiagram([], missing, parts, ids)
|
||||
end
|
||||
|
||||
function particle_after_tie(p::FeynmanParticle, t::FeynmanTie)
|
||||
if p == t.in1 || p == t.in2
|
||||
return FeynmanParticle(FermionStateful{Incoming}, -1) # placeholder particle and id for tied particles
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
function vertex_after_tie(v::FeynmanVertex, t::FeynmanTie)
|
||||
return FeynmanVertex(particle_after_tie(v.in1, t), particle_after_tie(v.in2, t), particle_after_tie(v.out, t))
|
||||
end
|
||||
|
||||
function vertex_after_tie(v::FeynmanVertex, t::Missing)
|
||||
return v
|
||||
end
|
||||
|
||||
function vertex_set_after_tie(vs::Set{FeynmanVertex}, t::FeynmanTie)
|
||||
return Set{FeynmanVertex}(vertex_after_tie(v, t) for v in vs)
|
||||
end
|
||||
|
||||
function vertex_set_after_tie(vs::Set{FeynmanVertex}, t::Missing)
|
||||
return vs
|
||||
end
|
||||
|
||||
function vertex_set_after_tie(vs::Set{FeynmanVertex}, t1::Union{FeynmanTie, Missing}, t2::Union{FeynmanTie, Missing})
|
||||
return Set{FeynmanVertex}(vertex_after_tie(vertex_after_tie(v, t1), t2) for v in vs)
|
||||
end
|
||||
|
||||
"""
|
||||
String(p::FeynmanParticle)
|
||||
|
||||
Return a string representation of the [`FeynmanParticle`](@ref) in a format that is readable by [`type_index_from_name`](@ref).
|
||||
"""
|
||||
function String(p::FeynmanParticle)
|
||||
return "$(String(p.particle))$(String(direction(p.particle)))$(p.id)"
|
||||
end
|
||||
|
||||
function hash(v::FeynmanVertex)
|
||||
return hash(v.in1) * hash(v.in2)
|
||||
end
|
||||
|
||||
function hash(t::FeynmanTie)
|
||||
return hash(t.in1) * hash(t.in2)
|
||||
end
|
||||
|
||||
function hash(d::FeynmanDiagram)
|
||||
return hash((d.vertices, d.particles))
|
||||
end
|
||||
|
||||
function ==(v1::FeynmanVertex, v2::FeynmanVertex)
|
||||
return (v1.in1 == v2.in1 && v1.in2 == v2.in1) || (v1.in2 == v2.in1 && v1.in1 == v2.in2)
|
||||
end
|
||||
|
||||
function ==(t1::FeynmanTie, t2::FeynmanTie)
|
||||
return (t1.in1 == t2.in1 && t1.in2 == t2.in1) || (t1.in2 == t2.in1 && t1.in1 == t2.in2)
|
||||
end
|
||||
|
||||
function ==(d1::FeynmanDiagram, d2::FeynmanDiagram)
|
||||
if (!ismissing(d1.tie[]) && ismissing(d2.tie[])) || (ismissing(d1.tie[]) && !ismissing(d2.tie[]))
|
||||
return false
|
||||
end
|
||||
if d1.particles != d2.particles
|
||||
return false
|
||||
end
|
||||
if length(d1.vertices) != length(d2.vertices)
|
||||
return false
|
||||
end
|
||||
|
||||
# TODO can i prove that this works?
|
||||
for (v1, v2) in zip(d1.vertices, d2.vertices)
|
||||
if vertex_set_after_tie(v1, d1.tie[], d2.tie[]) != vertex_set_after_tie(v2, d1.tie[], d2.tie[])
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
|
||||
#=return isequal.(
|
||||
vertex_set_after_tie(d1.vertices, d1.tie, d2.tie),
|
||||
vertex_set_after_tie(d2.vertices, d1.tie, d2.tie),
|
||||
)=#
|
||||
end
|
||||
|
||||
copy(fd::FeynmanDiagram) =
|
||||
FeynmanDiagram(deepcopy(fd.vertices), copy(fd.tie[]), deepcopy(fd.particles), copy(fd.type_ids))
|
||||
|
||||
"""
|
||||
id_for_type(d::FeynmanDiagram, t::Type{<:QEDParticle})
|
||||
|
||||
Return the highest id of any particle of the given type in the diagram + 1.
|
||||
"""
|
||||
function id_for_type(d::FeynmanDiagram, t::Type{<:QEDParticle})
|
||||
return d.type_ids[t] + 1
|
||||
end
|
||||
|
||||
"""
|
||||
can_apply_vertex(particles::Vector{FeynmanParticle}, vertex::FeynmanVertex)
|
||||
|
||||
Return true if the given [`FeynmanVertex`](@ref) can be applied to the given particles, i.e. both input particles of the vertex are in the vector and the output particle is not.
|
||||
"""
|
||||
function can_apply_vertex(particles::Vector{FeynmanParticle}, vertex::FeynmanVertex)
|
||||
return vertex.in1 in particles && vertex.in2 in particles && !(vertex.out in particles)
|
||||
end
|
||||
|
||||
"""
|
||||
apply_vertex!(particles::Vector{FeynmanParticle}, vertex::FeynmanVertex)
|
||||
|
||||
Apply a [`FeynmanVertex`](@ref) to the given vector of [`FeynmanParticle`](@ref)s.
|
||||
"""
|
||||
function apply_vertex!(particles::Vector{FeynmanParticle}, vertex::FeynmanVertex)
|
||||
#@assert can_apply_vertex(particles, vertex)
|
||||
length_before = length(particles)
|
||||
filter!(x -> x != vertex.in1 && x != vertex.in2, particles)
|
||||
push!(particles, vertex.out)
|
||||
#@assert length(particles) == length_before - 1
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
can_apply_tie(particles::Vector{FeynmanParticle}, tie::FeynmanTie)
|
||||
|
||||
Return true if the given [`FeynmanTie`](@ref) can be applied to the given particles, i.e. both input particles of the tie are in the vector.
|
||||
"""
|
||||
function can_apply_tie(particles::Vector{FeynmanParticle}, tie::FeynmanTie)
|
||||
return tie.in1 in particles && tie.in2 in particles
|
||||
end
|
||||
|
||||
"""
|
||||
apply_tie!(particles::Vector{FeynmanParticle}, tie::FeynmanTie)
|
||||
|
||||
Apply a [`FeynmanTie`](@ref) to the given vector of [`FeynmanParticle`](@ref)s.
|
||||
"""
|
||||
function apply_tie!(particles::Vector{FeynmanParticle}, tie::FeynmanTie)
|
||||
@assert length(particles) == 2
|
||||
@assert can_apply_tie(particles, tie)
|
||||
@assert can_tie(tie.in1.particle, tie.in2.particle)
|
||||
empty!(particles)
|
||||
@assert length(particles) == 0
|
||||
return nothing
|
||||
end
|
||||
|
||||
function apply_tie!(::Vector{FeynmanParticle}, ::Missing)
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
get_particles(fd::FeynmanDiagram, level::Int)
|
||||
|
||||
Return a vector of the particles after applying the vertices and tie of the diagram up to the given level. If no level is given, apply all. The tie comes last and is its own "level".
|
||||
"""
|
||||
function get_particles(fd::FeynmanDiagram, level::Int = -1)
|
||||
if level == -1
|
||||
level = length(fd.vertices) + 1
|
||||
end
|
||||
|
||||
working_particles = copy(fd.particles)
|
||||
for l in 1:length(fd.vertices)
|
||||
if l > level
|
||||
break
|
||||
end
|
||||
for v in fd.vertices[l]
|
||||
apply_vertex!(working_particles, v)
|
||||
end
|
||||
end
|
||||
|
||||
if (level > length(fd.vertices))
|
||||
apply_tie!(working_particles, fd.tie[])
|
||||
end
|
||||
|
||||
return working_particles
|
||||
end
|
||||
|
||||
"""
|
||||
add_vertex!(fd::FeynmanDiagram, vertex::FeynmanVertex)
|
||||
|
||||
Add the given vertex to the diagram, at the earliest level possible.
|
||||
"""
|
||||
function add_vertex!(fd::FeynmanDiagram, vertex::FeynmanVertex)
|
||||
for i in eachindex(fd.vertices)
|
||||
if (can_apply_vertex(get_particles(fd, i - 1), vertex))
|
||||
push!(fd.vertices[i], vertex)
|
||||
fd.type_ids[vertex.out.particle] += 1
|
||||
return nothing
|
||||
end
|
||||
end
|
||||
|
||||
if !can_apply_vertex(get_particles(fd), vertex)
|
||||
#@assert false "Can't add vertex $vertex to diagram"
|
||||
end
|
||||
|
||||
push!(fd.vertices, Set{FeynmanVertex}())
|
||||
push!(fd.vertices[end], vertex)
|
||||
fd.type_ids[vertex.out.particle] += 1
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
add_vertex(fd::FeynmanDiagram, vertex::FeynmanVertex)
|
||||
|
||||
Add the given vertex to the diagram, at the earliest level possible. Return the new diagram without muting the given one.
|
||||
"""
|
||||
function add_vertex(fd::FeynmanDiagram, vertex::FeynmanVertex)
|
||||
newfd = copy(fd)
|
||||
add_vertex!(newfd, vertex)
|
||||
return newfd
|
||||
end
|
||||
|
||||
"""
|
||||
add_tie!(fd::FeynmanDiagram, tie::FeynmanTie)
|
||||
|
||||
Add the given tie to the diagram, always at the last level.
|
||||
"""
|
||||
function add_tie!(fd::FeynmanDiagram, tie::FeynmanTie)
|
||||
if !can_apply_tie(get_particles(fd), tie)
|
||||
@assert false "Can't add tie $tie to diagram"
|
||||
end
|
||||
|
||||
fd.tie[] = tie
|
||||
#=
|
||||
@assert length(fd.vertices) >= 2
|
||||
#if the last vertex is involved in the tie and alone, lower it one level down
|
||||
if (length(fd.vertices[end]) != 1)
|
||||
return nothing
|
||||
end
|
||||
|
||||
vert = fd.vertices[end][1]
|
||||
if (vert != vertex_after_tie(vert, tie))
|
||||
return nothing
|
||||
end
|
||||
|
||||
pop!(fd.vertices)
|
||||
push!(fd.vertices[end], vert)
|
||||
=#
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
add_tie(fd::FeynmanDiagram, tie::FeynmanTie)
|
||||
|
||||
Add the given tie to the diagram, at the earliest level possible. Return the new diagram without muting the given one.
|
||||
"""
|
||||
function add_tie(fd::FeynmanDiagram, tie::FeynmanTie)
|
||||
newfd = copy(fd)
|
||||
add_tie!(newfd, tie)
|
||||
return newfd
|
||||
end
|
||||
|
||||
"""
|
||||
isvalid(fd::FeynmanDiagram)
|
||||
|
||||
Return whether the given diagram is valid. A diagram is valid iff the following are true:
|
||||
- After applying all vertices and the tie, there are no more particles left
|
||||
- The diagram is connected
|
||||
"""
|
||||
function isvalid(fd::FeynmanDiagram)
|
||||
if ismissing(fd.tie[])
|
||||
# diagram is connected iff there is one tie
|
||||
return false
|
||||
end
|
||||
|
||||
if get_particles(fd) != []
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
"""
|
||||
possible_vertices(fd::FeynmanDiagram)
|
||||
|
||||
Return a vector of all possible vertices that can be applied to the diagram at its current state.
|
||||
"""
|
||||
function possible_vertices(fd::FeynmanDiagram)
|
||||
possibilities = Vector{FeynmanVertex}()
|
||||
fully_generated_particles = get_particles(fd)
|
||||
|
||||
min_level = max(0, length(fd.vertices) - 1)
|
||||
for l in min_level:length(fd.vertices)
|
||||
particles = get_particles(fd, l)
|
||||
for i in 1:length(particles)
|
||||
for j in (i + 1):length(particles)
|
||||
p1 = particles[i]
|
||||
p2 = particles[j]
|
||||
if (caninteract(p1.particle, p2.particle))
|
||||
interaction_res = propagation_result(interaction_result(p1.particle, p2.particle))
|
||||
v = FeynmanVertex(p1, p2, FeynmanParticle(interaction_res, id_for_type(fd, interaction_res)))
|
||||
#@assert !(v.out in particles) "$v is in $fd"
|
||||
if !can_apply_vertex(fully_generated_particles, v)
|
||||
continue
|
||||
end
|
||||
push!(possibilities, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
if (!isempty(possibilities))
|
||||
return possibilities
|
||||
end
|
||||
end
|
||||
return possibilities
|
||||
end
|
||||
|
||||
"""
|
||||
can_tie(p1::Type, p2::Type)
|
||||
|
||||
For two given [`QEDParitcle`](@ref) types, return whether they can be tied together.
|
||||
|
||||
They can be tied iff one is the [`propagation_result`](@ref) of the other, or if both are photons, in which case their direction does not matter.
|
||||
"""
|
||||
function can_tie(p1::Type, p2::Type)
|
||||
if p1 == propagation_result(p2)
|
||||
return true
|
||||
end
|
||||
if (p1 <: PhotonStateful && p2 <: PhotonStateful)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
"""
|
||||
possible_tie(fd::FeynmanDiagram)
|
||||
|
||||
Return a possible tie or `missing` for the diagram at its current state.
|
||||
"""
|
||||
function possible_tie(fd::FeynmanDiagram)
|
||||
particles = get_particles(fd)
|
||||
if (length(particles) != 2)
|
||||
return missing
|
||||
end
|
||||
|
||||
if (particles[1] in fd.particles || particles[2] in fd.particles)
|
||||
return missing
|
||||
end
|
||||
|
||||
tie = FeynmanTie(particles[1], particles[2])
|
||||
if (can_apply_tie(particles, tie))
|
||||
return tie
|
||||
end
|
||||
return missing
|
||||
end
|
||||
|
||||
function remove_duplicates(compare_set::Set{FeynmanDiagram})
|
||||
result = Set()
|
||||
|
||||
while !isempty(compare_set)
|
||||
x = pop!(compare_set)
|
||||
# we know there will only be one duplicate if any, so search for that and delete it
|
||||
for y in compare_set
|
||||
if x == y
|
||||
delete!(compare_set, y)
|
||||
break
|
||||
end
|
||||
end
|
||||
push!(result, x)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
gen_diagrams(fd::FeynmanDiagram)
|
||||
|
||||
From a given feynman diagram in its initial state, e.g. when created through the [`FeynmanDiagram(pd::ProcessDescription)`](@ref) constructor, generate and return all possible [`FeynmanDiagram`](@ref)s that describe that process.
|
||||
"""
|
||||
function gen_diagrams(fd::FeynmanDiagram)
|
||||
working = Set{FeynmanDiagram}()
|
||||
results = Set{FeynmanDiagram}()
|
||||
|
||||
push!(working, fd)
|
||||
|
||||
# we know there will be particle_number - 2 vertices, followed by 1 tie
|
||||
n_particles = length(fd.particles)
|
||||
n_vertices = n_particles - 2
|
||||
|
||||
# doing this in iterations should reduce the intermediate number of diagrams by hash collisions
|
||||
for _ in 1:n_vertices
|
||||
next_iter_set = Set{FeynmanDiagram}()
|
||||
|
||||
while !isempty(working)
|
||||
d = pop!(working)
|
||||
|
||||
possibilities = possible_vertices(d)
|
||||
for v in possibilities
|
||||
push!(next_iter_set, add_vertex(d, v))
|
||||
end
|
||||
end
|
||||
|
||||
working = next_iter_set
|
||||
end
|
||||
|
||||
# add the tie
|
||||
for d in working
|
||||
tie = possible_tie(d)
|
||||
if ismissing(tie)
|
||||
continue
|
||||
end
|
||||
add_tie!(d, tie)
|
||||
if isvalid(d)
|
||||
push!(results, d)
|
||||
end
|
||||
end
|
||||
|
||||
return remove_duplicates(results)
|
||||
end
|
44
src/models/qed/parse.jl
Normal file
44
src/models/qed/parse.jl
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
"""
|
||||
parse_process(string::AbstractString, model::QEDModel)
|
||||
|
||||
Parse a string representation of a process, such as "ke->ke" into the corresponding [`QEDProcessDescription`](@ref).
|
||||
"""
|
||||
function parse_process(str::AbstractString, model::QEDModel)
|
||||
inParticles = Dict{Type, Int}()
|
||||
outParticles = Dict{Type, Int}()
|
||||
|
||||
if !(contains(str, "->"))
|
||||
throw("Did not find -> while parsing process \"$str\"")
|
||||
end
|
||||
|
||||
(inStr, outStr) = split(str, "->")
|
||||
|
||||
if (isempty(inStr) || isempty(outStr))
|
||||
throw("Process (\"$str\") input or output part is empty!")
|
||||
end
|
||||
|
||||
for t in types(model)
|
||||
if (isincoming(t))
|
||||
inCount = count(x -> x == String(t)[1], inStr)
|
||||
|
||||
if inCount != 0
|
||||
inParticles[t] = inCount
|
||||
end
|
||||
end
|
||||
if (isoutgoing(t))
|
||||
outCount = count(x -> x == String(t)[1], outStr)
|
||||
if outCount != 0
|
||||
outParticles[t] = outCount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if length(inStr) != sum(values(inParticles))
|
||||
throw("Encountered unknown characters in the input part of process \"$str\"")
|
||||
elseif length(outStr) != sum(values(outParticles))
|
||||
throw("Encountered unknown characters in the output part of process \"$str\"")
|
||||
end
|
||||
|
||||
return QEDProcessDescription(inParticles, outParticles)
|
||||
end
|
348
src/models/qed/particle.jl
Normal file
348
src/models/qed/particle.jl
Normal file
@ -0,0 +1,348 @@
|
||||
using QEDprocesses
|
||||
import QEDbase.mass
|
||||
|
||||
# TODO check
|
||||
const e = sqrt(4π / 137)
|
||||
|
||||
"""
|
||||
QEDModel <: AbstractPhysicsModel
|
||||
|
||||
Singleton definition for identification of the QED-Model.
|
||||
"""
|
||||
struct QEDModel <: AbstractPhysicsModel end
|
||||
|
||||
"""
|
||||
QEDParticle
|
||||
|
||||
Base type for all particles in the [`QEDModel`](@ref).
|
||||
|
||||
Its template parameter specifies the particle's direction.
|
||||
|
||||
The concrete types contain singletons of the types that they are, like `Photon` and `Electron` from QEDbase, and their state descriptions.
|
||||
"""
|
||||
abstract type QEDParticle{Direction <: ParticleDirection} <: AbstractParticle end
|
||||
|
||||
"""
|
||||
QEDProcessDescription <: AbstractProcessDescription
|
||||
|
||||
A description of a process in the QED-Model. Contains the input and output particles.
|
||||
|
||||
See also: [`in_particles`](@ref), [`out_particles`](@ref), [`parse_process`](@ref)
|
||||
"""
|
||||
struct QEDProcessDescription <: AbstractProcessDescription
|
||||
inParticles::Dict{Type{<:QEDParticle{Incoming}}, Int}
|
||||
outParticles::Dict{Type{<:QEDParticle{Outgoing}}, Int}
|
||||
end
|
||||
|
||||
"""
|
||||
QEDProcessInput <: AbstractProcessInput
|
||||
|
||||
Input for a QED Process. Contains the [`QEDProcessDescription`](@ref) of the process it is an input for, and the values of the in and out particles.
|
||||
|
||||
See also: [`gen_process_input`](@ref)
|
||||
"""
|
||||
struct QEDProcessInput <: AbstractProcessInput
|
||||
process::QEDProcessDescription
|
||||
inParticles::Vector{QEDParticle}
|
||||
outParticles::Vector{QEDParticle}
|
||||
end
|
||||
|
||||
QEDParticleValue{ParticleType <: QEDParticle} = Union{
|
||||
ParticleValue{ParticleType, BiSpinor},
|
||||
ParticleValue{ParticleType, AdjointBiSpinor},
|
||||
ParticleValue{ParticleType, DiracMatrix},
|
||||
ParticleValue{ParticleType, SLorentzVector{Float64}},
|
||||
ParticleValue{ParticleType, ComplexF64},
|
||||
}
|
||||
|
||||
"""
|
||||
PhotonStateful <: QEDParticle
|
||||
|
||||
A photon of the [`QEDModel`](@ref) with its state.
|
||||
"""
|
||||
struct PhotonStateful{Direction <: ParticleDirection} <: QEDParticle{Direction}
|
||||
momentum::SFourMomentum
|
||||
# this will maybe change to the full polarization vector? or do i need both
|
||||
polarization::AbstractDefinitePolarization
|
||||
end
|
||||
|
||||
PhotonStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} =
|
||||
PhotonStateful{Direction}(mom, PolX()) # TODO: make allpol possible
|
||||
|
||||
PhotonStateful{Dir1}(ph::PhotonStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} =
|
||||
PhotonStateful{Dir1}(ph.momentum, ph.polarization)
|
||||
|
||||
"""
|
||||
FermionStateful <: QEDParticle
|
||||
|
||||
A fermion of the [`QEDModel`](@ref) with its state.
|
||||
"""
|
||||
struct FermionStateful{Direction <: ParticleDirection} <: QEDParticle{Direction}
|
||||
momentum::SFourMomentum
|
||||
spin::AbstractDefiniteSpin
|
||||
# TODO: mass for electron/muon/tauon representation?
|
||||
end
|
||||
|
||||
FermionStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} =
|
||||
FermionStateful{Direction}(mom, SpinUp()) # TODO: make allspin possible
|
||||
|
||||
FermionStateful{Dir1}(f::FermionStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} =
|
||||
FermionStateful{Dir1}(f.momentum, f.spin)
|
||||
|
||||
"""
|
||||
AntiFermionStateful <: QEDParticle
|
||||
|
||||
An anti-fermion of the [`QEDModel`](@ref) with its state.
|
||||
"""
|
||||
struct AntiFermionStateful{Direction <: ParticleDirection} <: QEDParticle{Direction}
|
||||
momentum::SFourMomentum
|
||||
spin::AbstractDefiniteSpin
|
||||
# TODO: mass for electron/muon/tauon representation?
|
||||
end
|
||||
|
||||
AntiFermionStateful{Direction}(mom::SFourMomentum) where {Direction <: ParticleDirection} =
|
||||
AntiFermionStateful{Direction}(mom, SpinUp()) # TODO: make allspin possible
|
||||
|
||||
AntiFermionStateful{Dir1}(f::AntiFermionStateful{Dir2}) where {Dir1 <: ParticleDirection, Dir2 <: ParticleDirection} =
|
||||
AntiFermionStateful{Dir1}(f.momentum, f.spin)
|
||||
|
||||
"""
|
||||
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle, T2 <: QEDParticle}
|
||||
|
||||
For two given particle types that can interact, return the third.
|
||||
"""
|
||||
function interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle, T2 <: QEDParticle}
|
||||
@assert false "Invalid interaction between particles of types $t1 and $t2"
|
||||
end
|
||||
|
||||
interaction_result(::Type{FermionStateful{Incoming}}, ::Type{FermionStateful{Outgoing}}) = PhotonStateful{Incoming}
|
||||
interaction_result(::Type{FermionStateful{Incoming}}, ::Type{AntiFermionStateful{Incoming}}) = PhotonStateful{Incoming}
|
||||
interaction_result(::Type{FermionStateful{Incoming}}, ::Type{<:PhotonStateful}) = FermionStateful{Outgoing}
|
||||
|
||||
interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{FermionStateful{Incoming}}) = PhotonStateful{Incoming}
|
||||
interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{AntiFermionStateful{Outgoing}}) = PhotonStateful{Incoming}
|
||||
interaction_result(::Type{FermionStateful{Outgoing}}, ::Type{<:PhotonStateful}) = FermionStateful{Incoming}
|
||||
|
||||
# antifermion mirror
|
||||
interaction_result(::Type{AntiFermionStateful{Incoming}}, t2::Type{<:QEDParticle}) =
|
||||
interaction_result(FermionStateful{Outgoing}, t2)
|
||||
interaction_result(::Type{AntiFermionStateful{Outgoing}}, t2::Type{<:QEDParticle}) =
|
||||
interaction_result(FermionStateful{Incoming}, t2)
|
||||
|
||||
# photon commutativity
|
||||
interaction_result(t1::Type{<:PhotonStateful}, t2::Type{<:QEDParticle}) = interaction_result(t2, t1)
|
||||
|
||||
# but prevent stack overflow
|
||||
function interaction_result(t1::Type{<:PhotonStateful}, t2::Type{<:PhotonStateful})
|
||||
@assert false "Invalid interaction between particles of types $t1 and $t2"
|
||||
end
|
||||
|
||||
"""
|
||||
propagation_result(t1::Type{T}) where {T <: QEDParticle}
|
||||
|
||||
Return the type of the inverted direction. E.g.
|
||||
"""
|
||||
propagation_result(::Type{FermionStateful{Incoming}}) = FermionStateful{Outgoing}
|
||||
propagation_result(::Type{FermionStateful{Outgoing}}) = FermionStateful{Incoming}
|
||||
propagation_result(::Type{AntiFermionStateful{Incoming}}) = AntiFermionStateful{Outgoing}
|
||||
propagation_result(::Type{AntiFermionStateful{Outgoing}}) = AntiFermionStateful{Incoming}
|
||||
propagation_result(::Type{PhotonStateful{Incoming}}) = PhotonStateful{Outgoing}
|
||||
propagation_result(::Type{PhotonStateful{Outgoing}}) = PhotonStateful{Incoming}
|
||||
|
||||
"""
|
||||
types(::QEDModel)
|
||||
|
||||
Return a Vector of the possible types of particle in the [`QEDModel`](@ref).
|
||||
"""
|
||||
function types(::QEDModel)
|
||||
return [
|
||||
PhotonStateful{Incoming},
|
||||
PhotonStateful{Outgoing},
|
||||
FermionStateful{Incoming},
|
||||
FermionStateful{Outgoing},
|
||||
AntiFermionStateful{Incoming},
|
||||
AntiFermionStateful{Outgoing},
|
||||
]
|
||||
end
|
||||
|
||||
# type piracy?
|
||||
String(::Type{Incoming}) = "Incoming"
|
||||
String(::Type{Outgoing}) = "Outgoing"
|
||||
|
||||
String(::Incoming) = "i"
|
||||
String(::Outgoing) = "o"
|
||||
|
||||
function String(::Type{<:PhotonStateful})
|
||||
return "k"
|
||||
end
|
||||
function String(::Type{<:FermionStateful})
|
||||
return "e"
|
||||
end
|
||||
function String(::Type{<:AntiFermionStateful})
|
||||
return "p"
|
||||
end
|
||||
|
||||
@inline particle(::PhotonStateful) = Photon()
|
||||
@inline particle(::FermionStateful) = Electron()
|
||||
@inline particle(::AntiFermionStateful) = Positron()
|
||||
|
||||
@inline momentum(p::PhotonStateful)::SFourMomentum = p.momentum
|
||||
@inline momentum(p::FermionStateful)::SFourMomentum = p.momentum
|
||||
@inline momentum(p::AntiFermionStateful)::SFourMomentum = p.momentum
|
||||
|
||||
@inline spin_or_pol(p::PhotonStateful)::AbstractPolarization = p.polarization
|
||||
@inline spin_or_pol(p::FermionStateful)::AbstractSpin = p.spin
|
||||
@inline spin_or_pol(p::AntiFermionStateful)::AbstractSpin = p.spin
|
||||
|
||||
@inline direction(::PhotonStateful{Dir}) where {Dir <: ParticleDirection} = Dir()
|
||||
@inline direction(::FermionStateful{Dir}) where {Dir <: ParticleDirection} = Dir()
|
||||
@inline direction(::AntiFermionStateful{Dir}) where {Dir <: ParticleDirection} = Dir()
|
||||
|
||||
@inline direction(::Type{PhotonStateful{Dir}}) where {Dir <: ParticleDirection} = Dir()
|
||||
@inline direction(::Type{FermionStateful{Dir}}) where {Dir <: ParticleDirection} = Dir()
|
||||
@inline direction(::Type{AntiFermionStateful{Dir}}) where {Dir <: ParticleDirection} = Dir()
|
||||
|
||||
@inline isincoming(::QEDParticle{Incoming}) = true
|
||||
@inline isincoming(::QEDParticle{Outgoing}) = false
|
||||
@inline isoutgoing(::QEDParticle{Incoming}) = false
|
||||
@inline isoutgoing(::QEDParticle{Outgoing}) = true
|
||||
|
||||
@inline isincoming(::Type{<:QEDParticle{Incoming}}) = true
|
||||
@inline isincoming(::Type{<:QEDParticle{Outgoing}}) = false
|
||||
@inline isoutgoing(::Type{<:QEDParticle{Incoming}}) = false
|
||||
@inline isoutgoing(::Type{<:QEDParticle{Outgoing}}) = true
|
||||
|
||||
@inline mass(::Type{<:FermionStateful}) = 1.0
|
||||
@inline mass(::Type{<:AntiFermionStateful}) = 1.0
|
||||
@inline mass(::Type{<:PhotonStateful}) = 0.0
|
||||
|
||||
@inline invert_momentum(p::FermionStateful{Dir}) where {Dir <: ParticleDirection} =
|
||||
FermionStateful{Dir}(-p.momentum, p.spin)
|
||||
@inline invert_momentum(p::AntiFermionStateful{Dir}) where {Dir <: ParticleDirection} =
|
||||
AntiFermionStateful{Dir}(-p.momentum, p.spin)
|
||||
@inline invert_momentum(k::PhotonStateful{Dir}) where {Dir <: ParticleDirection} =
|
||||
PhotonStateful{Dir}(-k.momentum, k.polarization)
|
||||
|
||||
|
||||
"""
|
||||
caninteract(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
|
||||
|
||||
For two given [`QEDParticle`](@ref) types, return whether they can interact at a vertex. This is equivalent to `!issame(T1, T2)`.
|
||||
|
||||
See also: [`issame`](@ref) and [`interaction_result`](@ref)
|
||||
"""
|
||||
function caninteract(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
|
||||
if (T1 == T2)
|
||||
return false
|
||||
end
|
||||
if (T1 <: PhotonStateful && T2 <: PhotonStateful)
|
||||
return false
|
||||
end
|
||||
|
||||
for (P1, P2) in [(T1, T2), (T2, T1)]
|
||||
if (P1 == FermionStateful{Incoming} && P2 == AntiFermionStateful{Outgoing})
|
||||
return false
|
||||
end
|
||||
if (P1 == FermionStateful{Outgoing} && P2 == AntiFermionStateful{Incoming})
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function type_index_from_name(::QEDModel, name::String)
|
||||
if startswith(name, "ki")
|
||||
return (PhotonStateful{Incoming}, parse(Int, name[3:end]))
|
||||
elseif startswith(name, "ko")
|
||||
return (PhotonStateful{Outgoing}, parse(Int, name[3:end]))
|
||||
elseif startswith(name, "ei")
|
||||
return (FermionStateful{Incoming}, parse(Int, name[3:end]))
|
||||
elseif startswith(name, "eo")
|
||||
return (FermionStateful{Outgoing}, parse(Int, name[3:end]))
|
||||
elseif startswith(name, "pi")
|
||||
return (AntiFermionStateful{Incoming}, parse(Int, name[3:end]))
|
||||
elseif startswith(name, "po")
|
||||
return (AntiFermionStateful{Outgoing}, parse(Int, name[3:end]))
|
||||
else
|
||||
throw("Invalid name for a particle in the QED model")
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
issame(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
|
||||
|
||||
For two given [`QEDParticle`](@ref) types, return whether they are equivalent for the purpose of a Feynman Diagram. That means e.g. an `Incoming` `AntiFermion` is the same as an `Outgoing` `Fermion`. This is equivalent to `!caninteract(T1, T2)`.
|
||||
|
||||
See also: [`caninteract`](@ref) and [`interaction_result`](@ref)
|
||||
"""
|
||||
function issame(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
|
||||
return !caninteract(T1, T2)
|
||||
end
|
||||
|
||||
"""
|
||||
QED_vertex()
|
||||
|
||||
Return the factor of a vertex in a QED feynman diagram.
|
||||
"""
|
||||
@inline function QED_vertex()::SLorentzVector{DiracMatrix}
|
||||
# Peskin-Schroeder notation
|
||||
return -1im * e * gamma()
|
||||
end
|
||||
|
||||
@inline function QED_inner_edge(p::QEDParticle)
|
||||
pos_mom = p.momentum
|
||||
return propagator(particle(p), pos_mom)
|
||||
end
|
||||
|
||||
"""
|
||||
QED_conserve_momentum(p1::QEDParticle, p2::QEDParticle)
|
||||
|
||||
Calculate and return a new particle from two given interacting ones at a vertex.
|
||||
"""
|
||||
function QED_conserve_momentum(p1::QEDParticle, p2::QEDParticle)
|
||||
#println("Conserving momentum of \n$(direction(p1)) $(p1)\n and \n$(direction(p2)) $(p2)")
|
||||
T3 = interaction_result(typeof(p1), typeof(p2))
|
||||
# TODO: probably also need to do something about the spin/pol
|
||||
p1_mom = p1.momentum
|
||||
if (typeof(direction(p1)) <: Outgoing)
|
||||
p1_mom *= -1
|
||||
end
|
||||
p2_mom = p2.momentum
|
||||
if (typeof(direction(p2)) <: Outgoing)
|
||||
p2_mom *= -1
|
||||
end
|
||||
|
||||
p3_mom = p1_mom + p2_mom
|
||||
if (typeof(direction(T3)) <: Incoming)
|
||||
return T3(-p3_mom)
|
||||
end
|
||||
return T3(p3_mom)
|
||||
end
|
||||
|
||||
"""
|
||||
model(::AbstractProcessDescription)
|
||||
|
||||
Return the model of this process description.
|
||||
"""
|
||||
model(::QEDProcessDescription) = QEDModel()
|
||||
model(::QEDProcessInput) = QEDModel()
|
||||
|
||||
==(p1::QEDProcessDescription, p2::QEDProcessDescription) =
|
||||
p1.inParticles == p2.inParticles && p1.outParticles == p2.outParticles
|
||||
|
||||
function in_particles(process::QEDProcessDescription)
|
||||
return process.inParticles
|
||||
end
|
||||
|
||||
function in_particles(input::QEDProcessInput)
|
||||
return input.inParticles
|
||||
end
|
||||
|
||||
function out_particles(process::QEDProcessDescription)
|
||||
return process.outParticles
|
||||
end
|
||||
|
||||
function out_particles(input::QEDProcessInput)
|
||||
return input.outParticles
|
||||
end
|
115
src/models/qed/print.jl
Normal file
115
src/models/qed/print.jl
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
"""
|
||||
show(io::IO, process::QEDProcessDescription)
|
||||
|
||||
Pretty print an [`QEDProcessDescription`](@ref) (no newlines).
|
||||
|
||||
```jldoctest
|
||||
julia> using MetagraphOptimization
|
||||
|
||||
julia> print(parse_process("ke->ke", QEDModel()))
|
||||
QED Process: 'ke->ke'
|
||||
|
||||
julia> print(parse_process("kk->ep", QEDModel()))
|
||||
QED Process: 'kk->ep'
|
||||
```
|
||||
"""
|
||||
function show(io::IO, process::QEDProcessDescription)
|
||||
# types() gives the types in order (QED) instead of random like keys() would
|
||||
print(io, "QED Process: \'")
|
||||
for type in types(QEDModel())
|
||||
for _ in 1:get(process.inParticles, type, 0)
|
||||
print(io, String(type))
|
||||
end
|
||||
end
|
||||
print(io, "->")
|
||||
for type in types(QEDModel())
|
||||
for _ in 1:get(process.outParticles, type, 0)
|
||||
print(io, String(type))
|
||||
end
|
||||
end
|
||||
print(io, "'")
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
show(io::IO, processInput::QEDProcessInput)
|
||||
|
||||
Pretty print an [`QEDProcessInput`](@ref) (with newlines).
|
||||
"""
|
||||
function show(io::IO, processInput::QEDProcessInput)
|
||||
println(io, "Input for $(processInput.process):")
|
||||
println(io, " $(length(processInput.inParticles)) Incoming particles:")
|
||||
for particle in processInput.inParticles
|
||||
println(io, " $particle")
|
||||
end
|
||||
println(io, " $(length(processInput.outParticles)) Outgoing Particles:")
|
||||
for particle in processInput.outParticles
|
||||
println(io, " $particle")
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
show(io::IO, particle::T) where {T <: QEDParticle}
|
||||
|
||||
Pretty print an [`QEDParticle`](@ref) (no newlines).
|
||||
"""
|
||||
function show(io::IO, particle::T) where {T <: QEDParticle}
|
||||
print(io, "$(String(typeof(particle))): $(particle.momentum)")
|
||||
return nothing
|
||||
end
|
||||
|
||||
"""
|
||||
show(io::IO, particle::FeynmanParticle)
|
||||
|
||||
Pretty print a [`FeynmanParticle`](@ref) (no newlines).
|
||||
"""
|
||||
show(io::IO, p::FeynmanParticle) = print(io, "$(String(p.particle))_$(String(direction(p.particle)))_$(p.id)")
|
||||
|
||||
"""
|
||||
show(io::IO, particle::FeynmanVertex)
|
||||
|
||||
Pretty print a [`FeynmanVertex`](@ref) (no newlines).
|
||||
"""
|
||||
show(io::IO, v::FeynmanVertex) = print(io, "$(v.in1) + $(v.in2) -> $(v.out)")
|
||||
|
||||
"""
|
||||
show(io::IO, particle::FeynmanTie)
|
||||
|
||||
Pretty print a [`FeynmanTie`](@ref) (no newlines).
|
||||
"""
|
||||
show(io::IO, t::FeynmanTie) = print(io, "$(t.in1) -- $(t.in2)")
|
||||
|
||||
"""
|
||||
show(io::IO, particle::FeynmanDiagram)
|
||||
|
||||
Pretty print a [`FeynmanDiagram`](@ref) (with newlines).
|
||||
"""
|
||||
function show(io::IO, d::FeynmanDiagram)
|
||||
print(io, "Initial Particles: [")
|
||||
first = true
|
||||
for p in d.particles
|
||||
if first
|
||||
first = false
|
||||
print(io, "$p")
|
||||
else
|
||||
print(io, ", $p")
|
||||
end
|
||||
end
|
||||
print(io, "]\n")
|
||||
for l in eachindex(d.vertices)
|
||||
print(io, " Virtuality Level $l Vertices: [")
|
||||
first = true
|
||||
for v in d.vertices[l]
|
||||
if first
|
||||
first = false
|
||||
print(io, "$v")
|
||||
else
|
||||
print(io, ", $v")
|
||||
end
|
||||
end
|
||||
print(io, "]\n")
|
||||
end
|
||||
return print(io, " Tie: $(d.tie[])\n")
|
||||
end
|
135
src/models/qed/properties.jl
Normal file
135
src/models/qed/properties.jl
Normal file
@ -0,0 +1,135 @@
|
||||
# TODO use correct numbers
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_S1)
|
||||
|
||||
Return the compute effort of an S1 task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_S1)::Float64 = 11.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_S2)
|
||||
|
||||
Return the compute effort of an S2 task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_S2)::Float64 = 12.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_U)
|
||||
|
||||
Return the compute effort of a U task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_U)::Float64 = 1.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_V)
|
||||
|
||||
Return the compute effort of a V task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_V)::Float64 = 6.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_P)
|
||||
|
||||
Return the compute effort of a P task.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_P)::Float64 = 0.0
|
||||
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_Sum)
|
||||
|
||||
Return the compute effort of a Sum task.
|
||||
|
||||
Note: This is a constant compute effort, even though sum scales with the number of its inputs. Since there is only ever a single sum node in a graph generated from the QED-Model,
|
||||
this doesn't matter.
|
||||
"""
|
||||
compute_effort(t::ComputeTaskQED_Sum)::Float64 = 1.0
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_S1)
|
||||
|
||||
Print the S1 task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_S1) = print(io, "ComputeS1")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_S2)
|
||||
|
||||
Print the S2 task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_S2) = print(io, "ComputeS2")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_P)
|
||||
|
||||
Print the P task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_P) = print(io, "ComputeP")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_U)
|
||||
|
||||
Print the U task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_U) = print(io, "ComputeU")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_V)
|
||||
|
||||
Print the V task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_V) = print(io, "ComputeV")
|
||||
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_Sum)
|
||||
|
||||
Print the sum task to io.
|
||||
"""
|
||||
show(io::IO, t::ComputeTaskQED_Sum) = print(io, "ComputeSum")
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_S1)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_S1 (always 1).
|
||||
"""
|
||||
children(::ComputeTaskQED_S1) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_S2)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_S2 (always 2).
|
||||
"""
|
||||
children(::ComputeTaskQED_S2) = 2
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_P)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_P (always 1).
|
||||
"""
|
||||
children(::ComputeTaskQED_P) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_U)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_U (always 1).
|
||||
"""
|
||||
children(::ComputeTaskQED_U) = 1
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_V)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_V (always 2).
|
||||
"""
|
||||
children(::ComputeTaskQED_V) = 2
|
||||
|
||||
"""
|
||||
children(::ComputeTaskQED_Sum)
|
||||
|
||||
Return the number of children of a ComputeTaskQED_Sum.
|
||||
"""
|
||||
children(t::ComputeTaskQED_Sum) = t.children_number
|
||||
|
||||
function add_child!(t::ComputeTaskQED_Sum)
|
||||
t.children_number += 1
|
||||
return nothing
|
||||
end
|
51
src/models/qed/types.jl
Normal file
51
src/models/qed/types.jl
Normal file
@ -0,0 +1,51 @@
|
||||
"""
|
||||
ComputeTaskQED_S1 <: AbstractComputeTask
|
||||
|
||||
S task with a single child.
|
||||
"""
|
||||
struct ComputeTaskQED_S1 <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskQED_S2 <: AbstractComputeTask
|
||||
|
||||
S task with two children.
|
||||
"""
|
||||
struct ComputeTaskQED_S2 <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskQED_P <: AbstractComputeTask
|
||||
|
||||
P task with no children.
|
||||
"""
|
||||
struct ComputeTaskQED_P <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskQED_V <: AbstractComputeTask
|
||||
|
||||
v task with two children.
|
||||
"""
|
||||
struct ComputeTaskQED_V <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskQED_U <: AbstractComputeTask
|
||||
|
||||
u task with a single child.
|
||||
"""
|
||||
struct ComputeTaskQED_U <: AbstractComputeTask end
|
||||
|
||||
"""
|
||||
ComputeTaskQED_Sum <: AbstractComputeTask
|
||||
|
||||
Task that sums all its inputs, n children.
|
||||
"""
|
||||
mutable struct ComputeTaskQED_Sum <: AbstractComputeTask
|
||||
children_number::Int
|
||||
end
|
||||
|
||||
"""
|
||||
QED_TASKS
|
||||
|
||||
Constant vector of all tasks of the QED-Model.
|
||||
"""
|
||||
QED_TASKS =
|
||||
[ComputeTaskQED_S1, ComputeTaskQED_S2, ComputeTaskQED_P, ComputeTaskQED_V, ComputeTaskQED_U, ComputeTaskQED_Sum]
|
@ -6,3 +6,12 @@ Print a string representation of the fused compute task to io.
|
||||
function show(io::IO, t::FusedComputeTask)
|
||||
return print(io, "ComputeFuse($(t.first_task), $(t.second_task))")
|
||||
end
|
||||
|
||||
"""
|
||||
show(io::IO, t::DataTask)
|
||||
|
||||
Print the data task to io.
|
||||
"""
|
||||
function show(io::IO, t::DataTask)
|
||||
return print(io, "Data", t.data)
|
||||
end
|
||||
|
@ -58,6 +58,29 @@ Return the data of a data task. Given by the task's `.data` field.
|
||||
"""
|
||||
data(t::AbstractDataTask)::Float64 = getfield(t, :data)
|
||||
|
||||
"""
|
||||
copy(t::DataTask)
|
||||
|
||||
Copy the data task and return it.
|
||||
"""
|
||||
copy(t::DataTask) = DataTask(t.data)
|
||||
|
||||
"""
|
||||
children(::DataTask)
|
||||
|
||||
Return the number of children of a data task (always 1).
|
||||
"""
|
||||
children(::DataTask) = 1
|
||||
|
||||
"""
|
||||
children(t::FusedComputeTask)
|
||||
|
||||
Return the number of children of a FusedComputeTask.
|
||||
"""
|
||||
function children(t::FusedComputeTask)
|
||||
return length(union(Set(t.t1_inputs), Set(t.t2_inputs)))
|
||||
end
|
||||
|
||||
"""
|
||||
data(t::AbstractComputeTask)
|
||||
|
||||
|
@ -19,6 +19,15 @@ The shared base type for any data task.
|
||||
"""
|
||||
abstract type AbstractDataTask <: AbstractTask end
|
||||
|
||||
"""
|
||||
DataTask <: AbstractDataTask
|
||||
|
||||
Task representing a specific data transfer.
|
||||
"""
|
||||
struct DataTask <: AbstractDataTask
|
||||
data::Float64
|
||||
end
|
||||
|
||||
"""
|
||||
FusedComputeTask{T1 <: AbstractComputeTask, T2 <: AbstractComputeTask} <: AbstractComputeTask
|
||||
|
||||
|
136
src/utility.jl
136
src/utility.jl
@ -103,3 +103,139 @@ function unroll_symbol_vector(vec::Vector)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# CODE FROM HERE BORROWED FROM SOURCE: https://codebase.helmholtz.cloud/qedsandbox/QEDphasespaces.jl/
|
||||
# use qedphasespaces directly once released
|
||||
#
|
||||
# quick and dirty implementation of the RAMBO algorithm
|
||||
#
|
||||
# reference:
|
||||
# * https://cds.cern.ch/record/164736/files/198601282.pdf
|
||||
# * https://www.sciencedirect.com/science/article/pii/0010465586901190
|
||||
####################
|
||||
|
||||
function generate_initial_moms(ss, masses)
|
||||
E1 = (ss^2 + masses[1]^2 - masses[2]^2) / (2 * ss)
|
||||
E2 = (ss^2 + masses[2]^2 - masses[1]^2) / (2 * ss)
|
||||
|
||||
rho1 = sqrt(E1^2 - masses[1]^2)
|
||||
rho2 = sqrt(E2^2 - masses[2]^2)
|
||||
|
||||
return [SFourMomentum(E1, 0, 0, rho1), SFourMomentum(E2, 0, 0, -rho2)]
|
||||
end
|
||||
|
||||
|
||||
Random.rand(rng::AbstractRNG, ::Random.SamplerType{SFourMomentum}) = SFourMomentum(rand(rng, 4))
|
||||
Random.rand(rng::AbstractRNG, ::Random.SamplerType{NTuple{N, Float64}}) where {N} = Tuple(rand(rng, N))
|
||||
|
||||
|
||||
function _transform_uni_to_mom(u1, u2, u3, u4)
|
||||
cth = 2 * u1 - 1
|
||||
sth = sqrt(1 - cth^2)
|
||||
phi = 2 * pi * u2
|
||||
q0 = -log(u3 * u4)
|
||||
qx = q0 * sth * cos(phi)
|
||||
qy = q0 * sth * sin(phi)
|
||||
qz = q0 * cth
|
||||
|
||||
return SFourMomentum(q0, qx, qy, qz)
|
||||
end
|
||||
|
||||
function _transform_uni_to_mom!(uni_mom, dest)
|
||||
u1, u2, u3, u4 = Tuple(uni_mom)
|
||||
cth = 2 * u1 - 1
|
||||
sth = sqrt(1 - cth^2)
|
||||
phi = 2 * pi * u2
|
||||
q0 = -log(u3 * u4)
|
||||
qx = q0 * sth * cos(phi)
|
||||
qy = q0 * sth * sin(phi)
|
||||
qz = q0 * cth
|
||||
|
||||
return dest = SFourMomentum(q0, qx, qy, qz)
|
||||
end
|
||||
|
||||
_transform_uni_to_mom(u1234::Tuple) = _transform_uni_to_mom(u1234...)
|
||||
_transform_uni_to_mom(u1234::SFourMomentum) = _transform_uni_to_mom(Tuple(u1234))
|
||||
|
||||
function generate_massless_moms(rng, n::Int)
|
||||
a = Vector{SFourMomentum}(undef, n)
|
||||
rand!(rng, a)
|
||||
return map(_transform_uni_to_mom, a)
|
||||
end
|
||||
|
||||
function generate_physical_massless_moms(rng, ss, n)
|
||||
r_moms = generate_massless_moms(rng, n)
|
||||
Q = sum(r_moms)
|
||||
M = sqrt(Q * Q)
|
||||
fac = -1 / M
|
||||
Qx = getX(Q)
|
||||
Qy = getY(Q)
|
||||
Qz = getZ(Q)
|
||||
bx = fac * Qx
|
||||
by = fac * Qy
|
||||
bz = fac * Qz
|
||||
gamma = getT(Q) / M
|
||||
a = 1 / (1 + gamma)
|
||||
x = ss / M
|
||||
|
||||
i = 1
|
||||
while i <= n
|
||||
mom = r_moms[i]
|
||||
mom0 = getT(mom)
|
||||
mom1 = getX(mom)
|
||||
mom2 = getY(mom)
|
||||
mom3 = getZ(mom)
|
||||
|
||||
bq = bx * mom1 + by * mom2 + bz * mom3
|
||||
|
||||
p0 = x * (gamma * mom0 + bq)
|
||||
px = x * (mom1 + bx * mom0 + a * bq * bx)
|
||||
py = x * (mom2 + by * mom0 + a * bq * by)
|
||||
pz = x * (mom3 + bz * mom0 + a * bq * bz)
|
||||
|
||||
r_moms[i] = SFourMomentum(p0, px, py, pz)
|
||||
i += 1
|
||||
end
|
||||
return r_moms
|
||||
end
|
||||
|
||||
function _to_be_solved(xi, masses, p0s, ss)
|
||||
sum = 0.0
|
||||
for (i, E) in enumerate(p0s)
|
||||
sum += sqrt(masses[i]^2 + xi^2 * E^2)
|
||||
end
|
||||
return sum - ss
|
||||
end
|
||||
|
||||
function _build_massive_momenta(xi, masses, massless_moms)
|
||||
vec = SFourMomentum[]
|
||||
i = 1
|
||||
while i <= length(massless_moms)
|
||||
massless_mom = massless_moms[i]
|
||||
k0 = sqrt(getT(massless_mom)^2 * xi^2 + masses[i]^2)
|
||||
|
||||
kx = xi * getX(massless_mom)
|
||||
ky = xi * getY(massless_mom)
|
||||
kz = xi * getZ(massless_mom)
|
||||
|
||||
push!(vec, SFourMomentum(k0, kx, ky, kz))
|
||||
|
||||
i += 1
|
||||
end
|
||||
return vec
|
||||
end
|
||||
|
||||
first_derivative(func) = x -> ForwardDiff.derivative(func, float(x))
|
||||
|
||||
|
||||
function generate_physical_massive_moms(rng, ss, masses; x0 = 0.1)
|
||||
n = length(masses)
|
||||
massless_moms = generate_physical_massless_moms(rng, ss, n)
|
||||
energies = getT.(massless_moms)
|
||||
f = x -> _to_be_solved(x, masses, energies, ss)
|
||||
xi = find_zero((f, first_derivative(f)), x0, Roots.Newton())
|
||||
return _build_massive_momenta(xi, masses, massless_moms)
|
||||
end
|
||||
|
Reference in New Issue
Block a user