|
| 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