Skip to content

Commit 7bd6fe1

Browse files
authored
Merge pull request #39 from cta-observatory/add_longitudinal
Add reader and fit function for longitudinal files
2 parents bdf5174 + 8ade117 commit 7bd6fe1

5 files changed

Lines changed: 1695 additions & 0 deletions

File tree

corsikaio/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
from .file import CorsikaFile, CorsikaCherenkovFile, CorsikaParticleFile
2+
from .longitudinal import read_longitudinal_distributions, longitudinal_fit_function
23
from .version import __version__
34

45

56
__all__ = [
67
'CorsikaFile',
78
'CorsikaCherenkovFile',
89
'CorsikaParticleFile',
10+
'read_longitudinal_distributions',
11+
'longitudinal_fit_function',
912
'as_dict',
1013
'__version__',
1114
]

corsikaio/longitudinal.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""
2+
Functions related to the CORSIKA longitudinal distribution
3+
"""
4+
import re
5+
import numpy as np
6+
7+
PARTICLE_HEADER_RE = re.compile(r"LONGITUDINAL DISTRIBUTION IN\s+(\d+)\s+(SLANT|VERTICAL)\s+STEPS OF\s+(\d+(?:.\d*)?) G\/CM\*\*2 FOR SHOWER\s+(\d+)")
8+
ENERGY_HEADER_RE = re.compile(r"LONGITUDINAL ENERGY DEPOSIT IN\s+(\d+)\s+(SLANT|VERTICAL)\s+STEPS OF\s+(\d+(?:.\d*)?) G\/CM\*\*2 FOR SHOWER\s+(\d+)")
9+
10+
ENERGY_COLUMNS = [
11+
"depth",
12+
"gamma", "em_ioniz", "em_cut", "mu_ioniz",
13+
"mu_cut", "hadr_ioniz", "hadr_cut", "neutrino", "sum"
14+
]
15+
PARTICLE_COLUMNS = [
16+
"depth",
17+
"gammas", "positrons", "electrons", "mu_plus", "mu_minus",
18+
"hadrons", "charged", "nuclei", "cherenkov"
19+
]
20+
21+
22+
def longitudinal_fit_function(depth, n_max, depth_0, depth_max, a, b, c):
23+
"""
24+
The function CORSIKA fits to the longitudinal distribution.
25+
26+
Parameters of this function are stored in the event end block
27+
in case the corresponding options of the LONGI keyword are set.
28+
29+
Use like this:
30+
31+
>>> depth = np.linspace(0, 1000, 100)
32+
>>> longitudinal_fit_function(depth, *event.end["longitudinal_fit_parameters"])
33+
"""
34+
denominator = (a + b * depth + c * depth**2)
35+
exponent = (depth_max - depth_0) / denominator
36+
power = ((depth - depth_0) / (depth_max - depth_0))**exponent
37+
exp = np.exp((depth_max - depth) / denominator)
38+
return n_max * power * exp
39+
40+
41+
def read_longitudinal_distributions(path):
42+
"""
43+
Read longitudinal profiles from CORSIKA longitudinal file.
44+
45+
This function returns a generator that iterates over air showers.
46+
47+
Parameters
48+
----------
49+
path : str, Path
50+
Path to CORSIKA longitudinal output file (DATXXXXXX.long)
51+
52+
Yields
53+
------
54+
longitudinal : dict
55+
Dict with the information for one air shower.
56+
Contains the longitudinal tables for "particles" and "energy_deposition"
57+
and the "parameters", "chi2_ndf" and "average_deviation" values of the
58+
fit to the distribution if available.
59+
"""
60+
first = True
61+
with open(path, "r") as f:
62+
try:
63+
line = f.readline().strip()
64+
except UnicodeDecodeError:
65+
raise IOError(f"Inputfile {path} does not seem to be a longitudinal file")
66+
67+
while line:
68+
match = PARTICLE_HEADER_RE.match(line)
69+
if not match:
70+
if first:
71+
raise IOError(f"Inputfile {path} does not seem to be a longitudinal file")
72+
else:
73+
raise IOError(f"Error reading file, expected header line, got: {line}")
74+
first = False
75+
76+
n_steps = int(match.group(1))
77+
longi = dict(
78+
shower=int(match.group(4)),
79+
n_steps=n_steps,
80+
slant=match.group(2) == "SLANT",
81+
step_width=float(match.group(3)),
82+
)
83+
84+
# skip line with names
85+
f.readline()
86+
longi["particles"] = np.genfromtxt(
87+
f, max_rows=n_steps, names=PARTICLE_COLUMNS, dtype=None
88+
)
89+
90+
line = f.readline().strip()
91+
match = ENERGY_HEADER_RE.match(line)
92+
if not match:
93+
raise IOError(f"Error reading file, expected energy deposition header line, got: {line}")
94+
n_steps = int(match.group(1))
95+
96+
# skip
97+
f.readline()
98+
longi["energy_deposition"] = np.genfromtxt(f, max_rows=n_steps, names=ENERGY_COLUMNS, dtype=None)
99+
100+
line = f.readline()
101+
if line.strip().startswith("FIT"):
102+
f.readline()
103+
parameters = f.readline().partition(" = ")[2]
104+
longi["parameters"] = np.array([float(p) for p in parameters.split()])
105+
106+
chi2_ndf = f.readline().partition(" = ")[2]
107+
longi["chi2_ndf"] = float(chi2_ndf)
108+
109+
deviation = f.readline().partition(" = ")[2]
110+
longi["average_deviation"] = float(deviation)
111+
112+
f.readline()
113+
yield longi
114+
line = f.readline().strip()

0 commit comments

Comments
 (0)