# Copyright (c) 2018-2024, Eduardo Rodrigues and Henry Schreiner.
#
# Distributed under the 3-clause BSD license, see accompanying file LICENSE
# or https://github.com/scikit-hep/decaylanguage for details.
"""
This is a GooFit adaptor for amplitude chain.
"""
from __future__ import annotations
from enum import Enum
import pandas as pd
from particle import SpinType
from particle.particle.utilities import programmatic_name
from ..utils import LineFailure
from .amplitudechain import LS, AmplitudeChain
[docs]
class SF_4Body(Enum):
DtoPP1_PtoSP2_StoP3P4 = 0
DtoPP1_PtoVP2_VtoP3P4 = 1
DtoV1V2_V1toP1P2_V2toP3P4_S = 2
DtoV1V2_V1toP1P2_V2toP3P4_P = 3
DtoV1V2_V1toP1P2_V2toP3P4_D = 4
DtoAP1_AtoVP2_VtoP3P4 = 5
DtoAP1_AtoVP2Dwave_VtoP3P4 = 6
DtoVS_VtoP1P2_StoP3P4 = 7
DtoV1P1_V1toV2P2_V2toP3P4 = 8
DtoAP1_AtoSP2_StoP3P4 = 9
DtoTP1_TtoVP2_VtoP3P4 = 10
FF_12_34_L1 = 11
FF_12_34_L2 = 12
FF_123_4_L1 = 13
FF_123_4_L2 = 14
ONE = 15
known_spinfactors = {
"DtoA1P1_A1toS2P2_S2toP3P4": (SF_4Body.DtoAP1_AtoSP2_StoP3P4,),
"DtoA1P1_A1toV2P2Dwave_V2toP3P4": (SF_4Body.DtoAP1_AtoVP2Dwave_VtoP3P4,),
"DtoA1P1_A1toV2P2_V2toP3P4": (SF_4Body.DtoAP1_AtoVP2Dwave_VtoP3P4,),
"DtoS1S2_S1toP1P2_S2toP3P4": (SF_4Body.ONE,),
"DtoT1P1_T1toV2P2_V2toP3P4": (SF_4Body.DtoTP1_TtoVP2_VtoP3P4,),
"DtoV1S2_V1toP1P2_S2toP3P4": (SF_4Body.DtoVS_VtoP1P2_StoP3P4,),
"DtoV1V2_V1toP1P2_V2toP3P4": (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_S,),
"DtoV1V2_V1toP1P2_V2toP3P4_D": (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_D,),
"DtoV1V2_V1toP1P2_V2toP3P4_P": (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_P,),
"Dtos1P1_s1toS2P2_S2toP3P4": (SF_4Body.DtoPP1_PtoSP2_StoP3P4,),
"Dtos1P1_s1toV2P2_V2toP3P4": (SF_4Body.DtoPP1_PtoVP2_VtoP3P4,),
}
def sprint(stype):
if stype in {SpinType.PseudoTensor, SpinType.PseudoScalar}:
return stype.name[6].lower()
return stype.name[0]
[docs]
class DecayStructure(Enum):
FF_12_34 = 0
FF_1_2_34 = 1
[docs]
class GooFitChain(AmplitudeChain):
"""
Class to read an AmpGen options file and return GooFit C++ code.
"""
__slots__ = ()
pars: pd.DataFrame = None
consts: pd.DataFrame = None
[docs]
@classmethod
def make_intro(cls, all_states):
"""
Write out definitions of constant variables and vectors to hold intermediate values.
"""
header = f" // Event type: {all_states[0]} -> "
header += " ".join(f"{b} ({a})" for a, b in enumerate(all_states[1:]))
header += """\n
std::vector<std::vector<Lineshape*>> line_factor_list;
std::vector<std::vector<SpinFactor*>> spin_factor_list;
std::vector<Amplitude*> amplitudes_list;
"""
final_particles = set(all_states)
for particle in final_particles:
name = particle.programmatic_name.upper()
header += f" constexpr fptype {name:8} {{ {particle.mass:<14.8g} }};\n"
header += "\n"
for particle in cls.all_particles - final_particles:
name = particle.programmatic_name
header += " Variable {name:15} {{ {nameQ:21}, {particle.mass:<10.8g} }};\n".format(
name=name + "_M", nameQ='"' + name + '_M"', particle=particle
)
header += " Variable {name:15} {{ {nameQ:21}, {particle.width:<10.8g} }};\n".format(
name=name + "_W", nameQ='"' + name + '_W"', particle=particle
)
header += "\n"
header += " DK3P_DI.meson_radius = 5;\n"
header += " DK3P_DI.particle_masses = {{{}}};\n".format(
", ".join(x.programmatic_name.upper() for x in all_states)
)
return header
@property
def decay_structure(self):
"""
Determine if decay proceeds via two resonances or cascade decay.
"""
if len(self[0]) == 2 and len(self[1]) == 2:
return DecayStructure.FF_12_34
return DecayStructure.FF_1_2_34
@property
def formfactor(self):
"""
Return form factor based on relative angular momentum L.
"""
norm = self.decay_structure == DecayStructure.FF_12_34
if self.L == 0:
return None
if self.L == 1:
return SF_4Body.FF_12_34_L1 if norm else SF_4Body.FF_123_4_L1
if self.L == 2:
return SF_4Body.FF_12_34_L2 if norm else SF_4Body.FF_123_4_L2
raise NotImplementedError(f"L = {self.L} is not implemented")
[docs]
def spindetails(self):
"""
Return string with spin structure.
"""
if self.decay_structure == DecayStructure.FF_12_34:
a = f"{sprint(self[0].particle.spin_type)}1"
b = f"{sprint(self[1].particle.spin_type)}2"
return (
"Dto{a}{b}_{a}toP1P2_{b}toP3P4"
+ (
"_{self.spinfactor}"
if self.spinfactor and self.spinfactor != "S"
else ""
)
).format(self=self, a=a, b=b)
a = f"{sprint(self[0].particle.spin_type)}1"
if self[0].daughters:
b = f"{sprint(self[0][0].particle.spin_type)}2"
else:
raise LineFailure(self, f"{self[0]} has no daughters")
wave = (
f"{self[0].spinfactor}wave"
if self[0].spinfactor and self[0].spinfactor != "S"
else ""
)
return f"Dto{a}P1_{a}to{b}P2{wave}_{b}toP3P4"
@property
def spinfactors(self):
"""
Check if the spin structure is known and return it together with the form factor.
"""
if self.spindetails() in known_spinfactors:
spinfactor = list(known_spinfactors[self.spindetails()])
if self.L > 0:
spinfactor.append(self.formfactor)
return spinfactor
raise LineFailure(
self,
f"Spinfactors not currently included!: {self.spindetails()}",
)
# if self.decay_structure == DecayStructure.FF_12_34 :
# if (self[0].particle.spin_type in {SpinType.Vector, SpinType.Axial}
# and self[1].particle.spin_type in {SpinType.Vector, SpinType.Axial}):
#
# if self.spinfactor == 'D':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_D, SF_4Body.FF_12_34_L2)
# elif self.spinfactor == 'P':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_P, SF_4Body.FF_12_34_L1)
# elif self.spinfactor == 'S':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_S,)
# else:
# if (self[0].particle.spin_type == SpinType.Axial and
# self[0][0].particle.spin_type == SpinType.Vector):
# if self.spinfactor == 'D':
# return (SF_4Body.DtoAP1_AtoVP2Dwave_VtoP3P4, SF_4Body.FF_12_34_L2)
# else:
# return (SF_4Body.DtoAP1_AtoVP2_VtoP3P4, SF_4Body.FF_12_34_L1) # L1?
[docs]
@classmethod
def make_pars(cls):
"""
Write out the parameters used in the amplitudes.
"""
headerlist = []
header = ""
for name, par in cls.pars.iterrows():
pname = programmatic_name(name)
if not par.fix:
headerlist.append(
f' Variable {pname} {{"{name}", {par.value}, {par.error} }};'
)
else:
headerlist.append(f' Variable {pname} {{"{name}", {par.value} }};')
def strip_pararray(pars, begin, convert=lambda x: x):
mysplines = pars.index[pars.index.str.contains(begin, regex=False)]
vals = convert(mysplines.str.slice(len(begin))).astype(int)
series = pd.Series(mysplines, vals).sort_index()
return ",\n".join(series.map(lambda x: " " + programmatic_name(x)))
if not GooFitChain.consts.empty:
splines = GooFitChain.consts.index[
GooFitChain.consts.index.str.contains("Spline")
]
splines = set(
splines.str.replace("::Spline::N", "")
.str.replace("::Spline::Min", "")
.str.replace("::Spline::Max", "")
)
for spline in splines:
header += (
"\n std::vector<Variable> "
+ programmatic_name(spline)
+ "_SplineArr {{\n"
)
header += strip_pararray(GooFitChain.pars, f"{spline}::Spline::Gamma::")
header += "\n }};\n"
f_scatt = GooFitChain.pars.index[GooFitChain.pars.index.str.contains("f_scatt")]
if len(f_scatt):
header += "\n std::vector<Variable> f_scatt {{\n"
header += strip_pararray(GooFitChain.pars, "f_scatt")
header += "\n }};\n"
IS_mat = GooFitChain.pars.index[GooFitChain.pars.index.str.contains("IS_p")]
if len(IS_mat):
names = ("pipi", "KK", "4pi", "EtaEta", "EtapEta", "mass")
def convert(x):
i = x.str.split("_").str[0]
j = x.str.split("_").str[1].map(names.index)
return i.astype(int) * 6 + j.astype(int)
header += "\n std::vector<Variable> IS_poles {{\n"
header += strip_pararray(GooFitChain.pars, "IS_p", convert)
header += "\n }};\n"
return "\n".join(headerlist) + "\n" + header
[docs]
def make_lineshape(self, structure, masses):
"""
Write out the line shapes. Each kind of line shape is treated separately.
"""
name = self.name
par = self.particle.programmatic_name
a = structure[0] + 1
b = structure[1] + 1
# order assignment
if a > b:
a, b = b, a
L = self.L
radius = 5.0 if "c" in self.particle.quarks.lower() else 1.5
if self.ls_enum == LS.RBW:
return f'new Lineshapes::RBW("{name}", {par}_M, {par}_W, {L}, {masses}, FF::BL2)'
if self.ls_enum == LS.GSpline:
min_ = self.__class__.consts.loc[f"{self.name}::Spline::Min", "value"]
max_ = self.__class__.consts.loc[f"{self.name}::Spline::Max", "value"]
N = self.__class__.consts.loc[f"{self.name}::Spline::N", "value"]
AdditionalVars = programmatic_name(self.name) + "_SplineArr"
return f"""new Lineshapes::GSpline("{name}", {par}_M, {par}_W, {L}, {masses}, FF::BL2,
{radius}, {AdditionalVars}, Lineshapes::spline_t({min_},{max_},{int(N)}))"""
if self.ls_enum == LS.kMatrix:
_, poleprod, pterm = self.lineshape.split(".")
is_pole = "true" if poleprod == "pole" else "false"
return f"""new Lineshapes::kMatrix("{name}", {pterm}, {is_pole},
sA_0, sA, s0_prod, s0_scatt,
f_scatt, IS_poles,
{par}_M, {par}_W, {L}, {masses}, FF::BL2, {radius})"""
if self.ls_enum == LS.FOCUS:
_, mod = self.lineshape.split(".")
return (
f'new Lineshapes::FOCUS("{name}", Lineshapes::FOCUS::Mod::{mod},'
f" {par}_M, {par}_W, {L}, {masses}, FF::BL2, {radius})"
)
raise NotImplementedError(f"Unimplemented GooFit Lineshape {self.ls_enum.name}")
[docs]
def make_spinfactor(self, final_states):
"""
Write out the spin factor and push it to a vector.
"""
spin_factors = self.spinfactors
intro = " spin_factor_list.push_back(std::vector<SpinFactor*>({\n"
factor = []
for structure in self.list_structure(final_states):
if not spin_factors:
factor.append(
f" // TODO: Spin factor not implemented yet for {self.spindetails()}"
)
else:
for spin_factor in spin_factors:
structure_list = ", ".join(map(str, structure))
factor.append(
f' new SpinFactor("SF", SF_4Body::{spin_factor.name:37}, {structure_list})'
)
exit_ = "\n }));\n"
return intro + ",\n".join(factor) + exit_
[docs]
def make_linefactor(self, final_states):
"""
Write out the line shape and push it to a vector.
"""
intro = " line_factor_list.push_back(std::vector<Lineshape*>{\n"
factor = []
for structure in self.list_structure(final_states):
if self.decay_structure == DecayStructure.FF_12_34:
mass1 = f"M_{structure[0]+1}{structure[1]+1}"
mass2 = f"M_{structure[2]+1}{structure[3]+1}"
else:
mass1 = f"M_{structure[0]+1}{structure[1]+1}_{structure[2]+1}"
mass2 = f"M_{structure[0]+1}{structure[1]+1}"
masses = [mass1, mass2]
for i_mass, sub in enumerate(self.vertexes):
factor.append(
" " + sub.make_lineshape(structure, masses[i_mass])
)
exit_ = "\n });\n"
return intro + ",\n".join(factor) + exit_
[docs]
def make_amplitude(self, final_states):
"""
Write out the amplitude and push it to a vector.
"""
n = len(self.list_structure(final_states))
fix = "true" if self.fix else "false"
return (
" amplitudes_list.push_back(new Amplitude{\n"
f' "{self!s}",\n'
f' mkvar("{self!s}_r", {fix}, {self.amp.real:.6}, {self.err.real:.6}),\n'
f' mkvar("{self!s}_i", {fix}, {self.amp.imag:.6}, {self.err.imag:.6}),\n'
" line_factor_list.back(),\n"
" spin_factor_list.back(),\n"
f" {n}}});\n\n"
" DK3P_DI.amplitudes_B.push_back(amplitudes_list.back());"
)
[docs]
def to_goofit(self, final_states):
"""
Write the vectors with the spin factors, line shapes and amplitudes.
"""
return (
" // "
+ str(self)
+ "\n\n"
+ self.make_spinfactor(final_states)
+ "\n"
+ self.make_linefactor(final_states)
+ "\n"
+ self.make_amplitude(final_states)
)
[docs]
@classmethod
def read_ampgen(cls, *args, **kargs):
"""
Read in an AmpGen file.
Returns
---------
Array of AmplitudeChains, event type.
"""
(
line_arr,
GooFitChain.pars,
GooFitChain.consts,
all_states,
) = super().read_ampgen(*args, **kargs)
return line_arr, all_states
[docs]
class GooFitPyChain(AmplitudeChain):
"""
Class to read an AmpGen options file and return a GooFit Python script.
"""
__slots__ = ()
pars: pd.DataFrame = None
consts: pd.DataFrame = None
[docs]
@classmethod
def make_intro(cls, all_states):
"""
Write out definitions of constant variables and lists to hold intermediate values.
"""
header = f"#Event type: {all_states[0]} -> "
header += " ".join(f"{b} ({a})" for a, b in enumerate(all_states[1:]))
header += "\nfrom goofit import *\n"
header += "\nDK3P_DI = DecayInfo4()\n"
header += (
"\nline_factor_list = []"
"\nspin_factor_list = []"
"\namplitudes_list = []\n"
)
final_particles = set(all_states)
for particle in final_particles:
name = particle.programmatic_name.upper()
header += f"{name:8} = {particle.mass:<14.8g}\n"
header += "\n"
for particle in cls.all_particles - final_particles:
name = particle.programmatic_name
header += (
"{name:15} = Variable({nameQ:21}, {particle.mass:<10.8g})\n".format(
name=name + "_M", nameQ='"' + name + '_M"', particle=particle
)
)
header += (
"{name:15} = Variable({nameQ:21}, {particle.width:<10.8g})\n".format(
name=name + "_W", nameQ='"' + name + '_W"', particle=particle
)
)
header += "\n"
header += "DK3P_DI.meson_radius = 5\n"
header += "DK3P_DI.particle_masses = ({})\n".format(
", ".join(x.programmatic_name.upper() for x in all_states)
)
return header
@property
def decay_structure(self):
"""
Determine if the decay proceeds via two resonances or a cascade decay.
"""
if len(self[0]) == 2 and len(self[1]) == 2:
return DecayStructure.FF_12_34
return DecayStructure.FF_1_2_34
@property
def formfactor(self):
"""
Return the form factor based on relative angular momentum L.
"""
norm = self.decay_structure == DecayStructure.FF_12_34
if self.L == 0:
return None
if self.L == 1:
return SF_4Body.FF_12_34_L1 if norm else SF_4Body.FF_123_4_L1
if self.L == 2:
return SF_4Body.FF_12_34_L2 if norm else SF_4Body.FF_123_4_L2
raise NotImplementedError(f"L = {self.L} is not implemented")
[docs]
def spindetails(self):
"""
Return string with spin structure.
"""
if self.decay_structure == DecayStructure.FF_12_34:
a = f"{sprint(self[0].particle.spin_type)}1"
b = f"{sprint(self[1].particle.spin_type)}2"
return (
"Dto{a}{b}_{a}toP1P2_{b}toP3P4"
+ (
"_{self.spinfactor}"
if self.spinfactor and self.spinfactor != "S"
else ""
)
).format(self=self, a=a, b=b)
a = f"{sprint(self[0].particle.spin_type)}1"
if self[0].daughters:
b = f"{sprint(self[0][0].particle.spin_type)}2"
else:
raise LineFailure(self, f"{self[0]} has no daughters")
wave = (
f"{self[0].spinfactor}wave"
if self[0].spinfactor and self[0].spinfactor != "S"
else ""
)
return f"Dto{a}P1_{a}to{b}P2{wave}_{b}toP3P4"
@property
def spinfactors(self):
"""
Check if the spin structure is known and return it together with the form factor.
"""
if self.spindetails() in known_spinfactors:
spinfactor = list(known_spinfactors[self.spindetails()])
if self.L > 0:
spinfactor.append(self.formfactor)
return spinfactor
raise LineFailure(
self,
f"Spinfactors not currently included!: {self.spindetails()}",
)
# if self.decay_structure == DecayStructure.FF_12_34 :
# if (self[0].particle.spin_type in {SpinType.Vector, SpinType.Axial}
# and self[1].particle.spin_type in {SpinType.Vector, SpinType.Axial}):
#
# if self.spinfactor == 'D':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_D, SF_4Body.FF_12_34_L2)
# elif self.spinfactor == 'P':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_P, SF_4Body.FF_12_34_L1)
# elif self.spinfactor == 'S':
# return (SF_4Body.DtoV1V2_V1toP1P2_V2toP3P4_S,)
# else:
# if (self[0].particle.spin_type == SpinType.Axial and
# self[0][0].particle.spin_type == SpinType.Vector):
# if self.spinfactor == 'D':
# return (SF_4Body.DtoAP1_AtoVP2Dwave_VtoP3P4, SF_4Body.FF_12_34_L2)
# else:
# return (SF_4Body.DtoAP1_AtoVP2_VtoP3P4, SF_4Body.FF_12_34_L1) # L1?
[docs]
@classmethod
def make_pars(cls):
"""
Write out the parameters used in the amplitudes.
"""
headerlist = []
header = ""
for name, par in cls.pars.iterrows():
pname = programmatic_name(name)
if not par.fix:
headerlist.append(
f'{pname} = Variable("{name}", {par.value}, {par.error} )'
)
else:
headerlist.append(f'{pname} = Variable("{name}", {par.value})')
def strip_pararray(pars, begin, convert=lambda x: x):
mysplines = pars.index[pars.index.str.contains(begin, regex=False)]
vals = convert(mysplines.str.slice(len(begin))).astype(int)
series = pd.Series(mysplines, vals).sort_index()
return ",\n".join(series.map(lambda x: " " + programmatic_name(x)))
if not GooFitPyChain.consts.empty:
splines = GooFitPyChain.consts.index[
GooFitPyChain.consts.index.str.contains("Spline")
]
splines = set(
splines.str.replace("::Spline::N", "")
.str.replace("::Spline::Min", "")
.str.replace("::Spline::Max", "")
)
for spline in splines:
header += "\n" + programmatic_name(spline) + "_SplineArr = [\n"
header += strip_pararray(
GooFitPyChain.pars, f"{spline}::Spline::Gamma::"
)
header += "]\n"
f_scatt = GooFitPyChain.pars.index[
GooFitPyChain.pars.index.str.contains("f_scatt")
]
if len(f_scatt):
header += "f_scatt = [\n"
header += strip_pararray(GooFitPyChain.pars, "f_scatt")
header += "]\n"
IS_mat = GooFitPyChain.pars.index[GooFitPyChain.pars.index.str.contains("IS_p")]
if len(IS_mat):
names = ("pipi", "KK", "4pi", "EtaEta", "EtapEta", "mass")
def convert(x):
i = x.str.split("_").str[0]
j = x.str.split("_").str[1].map(names.index)
return i.astype(int) * 6 + j.astype(int)
header += "\nIS_poles = [\n"
header += strip_pararray(GooFitPyChain.pars, "IS_p", convert)
header += "]\n"
return "\n".join(headerlist) + "\n" + header
[docs]
def make_lineshape(self, structure, masses):
"""
Write out the line shapes. Each kind of line shape is treated separately.
"""
name = self.name
par = self.particle.programmatic_name
a = structure[0] + 1
b = structure[1] + 1
# order assignment
if a > b:
a, b = b, a
L = int(self.L)
radius = 5.0 if "c" in self.particle.quarks.lower() else 1.5
if self.ls_enum == LS.RBW:
return f'Lineshapes.RBW("{name}", {par}_M, {par}_W, {L}, {masses}, FF.BL2)'
if self.ls_enum == LS.GSpline:
min_ = self.__class__.consts.loc[f"{self.name}::Spline::Min", "value"]
max_ = self.__class__.consts.loc[f"{self.name}::Spline::Max", "value"]
N = self.__class__.consts.loc[f"{self.name}::Spline::N", "value"]
AdditionalVars = programmatic_name(self.name) + "_SplineArr"
return f"""Lineshapes.GSpline("{name}", {par}_M, {par}_W, {L}, {masses}, FF.BL2,
{radius}, {AdditionalVars}, ({min_},{max_},{int(N)}))"""
if self.ls_enum == LS.kMatrix:
_, poleprod, pterm = self.lineshape.split(".")
is_pole = "True" if poleprod == "pole" else "False"
return f"""Lineshapes.kMatrix("{name}", {pterm}, {is_pole},
sA_0, sA, s0_prod, s0_scatt,
f_scatt, IS_poles,
{par}_M, {par}_W, {L}, {masses}, FF.BL2, {radius})"""
if self.ls_enum == LS.FOCUS:
_, mod = self.lineshape.split(".")
return (
f'Lineshapes.FOCUS("{name}", Lineshapes.FocusMod.{mod},'
f" {par}_M, {par}_W, {L}, {masses}, FF.BL2, {radius})"
)
raise NotImplementedError(f"Unimplemented GooFit Lineshape {self.ls_enum.name}")
[docs]
def make_spinfactor(self, final_states):
"""
Write out the spin factor and push it to a vector.
"""
spin_factors = self.spinfactors
intro = "spin_factor_list.append((\n"
factor = []
for structure in self.list_structure(final_states):
if not spin_factors:
factor.append(
f" // TODO: Spin factor not implemented yet for {self.spindetails()}"
)
else:
for spin_factor in spin_factors:
structure_list = ", ".join(map(str, structure))
factor.append(
f' SpinFactor("SF", SF_4Body.{spin_factor.name:37}, {structure_list})'
)
exit_ = "))\n"
return intro + ",\n".join(factor) + exit_
[docs]
def make_linefactor(self, final_states):
"""
Write out the line shape and push it to a vector.
"""
intro = "line_factor_list.append((\n"
factor = []
for structure in self.list_structure(final_states):
if self.decay_structure == DecayStructure.FF_12_34:
mass1 = f"M_{structure[0]+1}{structure[1]+1}"
mass2 = f"M_{structure[2]+1}{structure[3]+1}"
else:
mass1 = f"M_{structure[0]+1}{structure[1]+1}_{structure[2]+1}"
mass2 = f"M_{structure[0]+1}{structure[1]+1}"
masses = [mass1, mass2]
for i_mass, sub in enumerate(self.vertexes):
factor.append(
" " + sub.make_lineshape(structure, masses[i_mass])
)
exit_ = "))\n"
return intro + ",\n".join(factor) + exit_
[docs]
def make_amplitude(self, final_states):
"""
Write out the amplitude and push it to a vector.
"""
n = len(self.list_structure(final_states))
real_coeff = (
f'Variable("{self!s}_r", {self.amp.real:.6})'
if self.fix
else f'Variable("{self!s}_r", {self.amp.real:.6},{self.err.real:.6}, 0., 1000.)'
)
imag_coeff = (
f'Variable("{self!s}_i", {self.amp.imag:.6})'
if self.fix
else f'Variable("{self!s}_r", {self.amp.imag:.6},{self.err.imag:.6}, 0., 1000.)'
)
return (
"amplitudes_list.append(Amplitude(\n"
f' "{self!s}",\n'
f" {real_coeff},\n"
f" {imag_coeff},\n"
" line_factor_list[-1],\n"
" spin_factor_list[-1],\n"
f" {n}))\n\n"
)
[docs]
def to_goofit(self, final_states):
"""
Write the lists with the spin factors, line shapes and amplitudes.
"""
return (
"#"
+ str(self)
+ "\n\n"
+ self.make_spinfactor(final_states)
+ "\n"
+ self.make_linefactor(final_states)
+ "\n"
+ self.make_amplitude(final_states)
+ "\n"
)
[docs]
@classmethod
def read_ampgen(cls, *args, **kargs):
"""
Read in the AmpGen file.
Returns
---------
Array of AmplitudeChains, event type.
"""
(
line_arr,
GooFitPyChain.pars,
GooFitPyChain.consts,
all_states,
) = super().read_ampgen(*args, **kargs)
return line_arr, all_states