diff --git a/km3buu/physics.py b/km3buu/physics.py new file mode 100644 index 0000000000000000000000000000000000000000..b1e7e06aba4b24687482c52a13e04b009c1ab6f6 --- /dev/null +++ b/km3buu/physics.py @@ -0,0 +1,247 @@ +# Filename: physics.py +""" +Additional physics functionality + +""" + +__author__ = "Johannes Schumann" +__copyright__ = "Copyright 2021, Johannes Schumann and the KM3NeT collaboration." +__credits__ = [] +__license__ = "MIT" +__maintainer__ = "Johannes Schumann" +__email__ = "jschumann@km3net.de" +__status__ = "Development" + +ELEC_PARAMS = { + "ELECa": 1.33356e5, + "ELECb": 1.66113e2, + "ELECc": 16.4949, + "ELECd": 1.5385e5, + "ELECe": 6.04871e5, +} + +PION_PARAMS = { + "PIa": 0.538346, + "PIb": 1.32041, + "PIc": 0.737415, + "PId": -0.813861, + "PIe": -2.22444, + "PIf": -2.15795, + "PIg": -6.47242, + "PIh": -2.7567, + "PIx": 8.83498, +} + +KAON_PARAMS = { + "Ka": 12.7537, + "Kb": -1.052, + "Kc": 3.49559, + "Kd": 16.7914, + "Ke": -0.355066, + "Kf": 2.77116, +} + +KSHORT_PARAMS = { + "K0sa": 0.351242, + "K0sb": 0.613076, + "K0sc": 6.24682, + "K0sd": 2.8858, +} + +KLONG_PARAMS = { + "K0la": 2.18152, + "K0lb": -0.632798, + "K0lc": 0.999514, + "K0ld": 1.36052, + "K0le": 4.22484, + "K0lf": -0.307859, +} + +PROTON_PARAMS = { + "Pa": 12.1281, + "Pb": -17.1528, + "Pc": 0.573158, + "Pd": 34.1436, + "Pe": -0.28944, + "Pf": -13.2619, + "Pg": 24.1357, +} + +NEUTRON_PARAMS = { + "Na": 1.24605, + "Nb": 0.63819, + "Nc": -0.802822, + "Nd": -0.935327, + "Ne": -6.1126, + "Nf": -1.96894, + "Ng": 0.716954, + "Nh": 2.68246, + "Ni": -9.39464, + "Nj": 15.0737, +} + +HE_PARAMS = { + "Ma": 72.425, + "Mb": -49.417, + "Mc": 5.858, + "Md": 207.252, + "Me": 132.784, + "Mf": -10.277, + "Mg": -19.441, + "Mh": 58.598, + "Mi": 53.161, + "Mkref": 2.698, +} + + +def visible_energy_fraction(pdgid, energy): + """ + Returns the visible energy fraction in the one particle approximation (OPA) + + Parameters + ---------- + pdgid: int + PDGID of the given particle + energy: float + Kinetic energy of the given particle + """ + + # Cover trivial cases, i.e. 'non-visible' particles and ultra-high energy + if pdgid in [11, -11, 22, 111, 221]: + return 1.0 + elif energy > 1e7: + return high_energy_weight(energy) + + tmp = energy if energy < 40. else 40. + weight = 0.0 + + if abs(pdgid) == 211: + weight = pion_weight(tmp) + elif pdgid == 130: + weight = klong_weight(tmp) + elif pdgid == 310: + weight = kshort_weight(tmp) + elif abs(pdgid) == 321: + weight = kaon_weight(tmp) + elif pdgid == 2112: + weight = neutron_weight(tmp) + elif pdgid == 2122: + weight = proton_weight(tmp) + elif pdgid in [12, -13, 13, 113, 213]: + weight = 0.0 + else: + weight = proton_weight(tmp) + + if energy < 40.: + return weight + else: + he_weight = high_energy_weight(energy) + he40GeV_weight = high_energy_weight(40.) + return he_weight - (he40GeV_weight - + weight) * (7. - np.log10(energy)) / 5.398 + + +def number_photons_per_electron(energy): + """Expected number of photons for electrons as function of energy""" + exp_coeff = np.exp(-energy / ELEC_PARAMS["ELECc"]) + n = (ELEC_PARAMS["ELECa"] * energy + ELEC_PARAMS["ELECb"]) * exp_coeff + ( + ELEC_PARAMS["ELECd"] * energy + ELEC_PARAMS["ELECe"]) * (1 - exp_coeff) + return n + + +def pion_weight(energy): + norm = nphotons_per_electron(energy) + if energy < 6e-2: + return 1e4 * PION_PARAMS["PIa"] / norm + elif energy < 1.5e-1: + return 1e4 * PION_PARAMS["PIx"] * energy / norm + else: + logE = np.log(energy) + return (1e5 * PION_PARAMS["PIb"] * energy + + (energy**(1. - 1. / PION_PARAMS["PIc"])) * + (PION_PARAMS["PId"] * 1e4 + 1e4 * PION_PARAMS["PIe"] * logE + + 1e4 * PION_PARAMS["PIf"] * logE**2 + 1e3 * PION_PARAMS["PIg"] + * logE**3 + 1e2 * PION_PARAMS["PIh"] * logE**4)) / norm + + +def kaon_weight(energy): + norm = number_photons_per_electron(energy) + if energy > 0.26: + exp_coeff = np.exp(-energy / KAON_PARAMS["Kc"]) + return (1e4 * KAON_PARAMS["Ka"] * (energy + KAON_PARAMS["Kb"]) * + (1. - exp_coeff) + 1e4 * + (KAON_PARAMS["Kd"] * energy + KAON_PARAMS["Ke"]) * + exp_coeff) / norm + else: + return KAON_PARAMS["Kf"] * 1e4 / norm + + +def kshort_weight(energy): + norm = number_photons_per_electron(energy) + return (KSHORT_PARAMS["K0sa"] * 1e5 + KSHORT_PARAMS["K0sb"] * 1e5 * energy + + energy * KSHORT_PARAMS["K0sc"] * 1e4 * + np.log(KSHORT_PARAMS["K0sd"] + 1. / energy)) / norm + + +def klong_weight(energy): + norm = number_photons_per_electron(energy) + if energy < 1.5: + return (1e4 * KLONG_PARAMS["K0la"] + + energy * 1e5 * KLONG_PARAMS["K0lb"] * np.log(energy) + + 1e5 * KLONG_PARAMS["K0lc"] * energy**1.5) / norm + else: + return (energy * KLONG_PARAMS["K0ld"] * 1e5 + + energy**(1. - 1. / KLONG_PARAMS["K0le"]) * + KLONG_PARAMS["K0lf"] * 1e5) / norm + + +def proton_weight(energy): + exp_coeff = np.exp(-energy / PROTON_PARAMS["Pc"]) + norm = number_photons_per_electron(energy) + weight = 1e4 * (PROTON_PARAMS["Pa"] * energy + PROTON_PARAMS["Pb"]) * ( + 1 - exp_coeff) + 1e4 * (PROTON_PARAMS["Pd"] * energy + + PROTON_PARAMS["Pe"] + + PROTON_PARAMS["Pf"] * energy**2 + + PROTON_PARAMS["Pg"] * energy**3) * exp_coeff + return weight / norm + + +def neutron_weight(energy): + norm = number_photons_per_electron(energy) + if energy > 0.5: + logE = np.log(energy) + return (NEUTRON_PARAMS["Na"] * 1e5 * energy + + 1e3 * energy**(1. - 1. / NEUTRON_PARAMS["Nb"]) * + (100 * NEUTRON_PARAMS["Nc"] + 100 * NEUTRON_PARAMS["Nd"] * logE + + 10 * NEUTRON_PARAMS["Ne"] * logE**2 + + 11 * NEUTRON_PARAMS["Nf"] * logE**3)) / norm + else: + return (1e3 * NEUTRON_PARAMS["Ng"] + 1e4 * NEUTRON_PARAMS["Nh"] * + energy + 1e4 * NEUTRON_PARAMS["Ni"] * energy**2 + + 1e4 * NEUTRON_PARAMS["Nj"] * energy**3) / norm + + +def high_energy_weight(energy): + """ + High energy weight (valid above 40 GeV) + + Parameters + ---------- + energy: float + Kinetic energy of the given particle + """ + if energy < 0.2: + return 0.292 + + logE = np.log10(energy) + mlc = (HE_PARAMS["Ma"] - HE_PARAMS["Mf"]) / HE_PARAMS["Mkref"] + denom = HE_PARAMS["Mi"] + HE_PARAMS["Mh"] * logE + HE_PARAMS[ + "Mg"] * logE**2 + HE_PARAMS["Mf"] * logE**3 + mlc * logE**4 + if denom <= 0: + return 0 + num = HE_PARAMS[ + "Me"] + HE_PARAMS["Md"] * logE + HE_PARAMS["Mc"] * logE**2 + HE_PARAMS[ + "Mb"] * logE**3 + HE_PARAMS["Ma"] * logE**4 + mlc * logE**5 + lognp = num / denom + Ee = 10.**(lognp - HE_PARAMS["Mkref"]) + return Ee / energy