reboost 0.8.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reboost/__init__.py +14 -0
- reboost/_version.py +34 -0
- reboost/build_evt.py +134 -0
- reboost/build_glm.py +305 -0
- reboost/build_hit.py +466 -0
- reboost/cli.py +194 -0
- reboost/core.py +526 -0
- reboost/daq/__init__.py +5 -0
- reboost/daq/core.py +262 -0
- reboost/daq/utils.py +28 -0
- reboost/hpge/__init__.py +0 -0
- reboost/hpge/psd.py +847 -0
- reboost/hpge/surface.py +284 -0
- reboost/hpge/utils.py +79 -0
- reboost/iterator.py +226 -0
- reboost/log_utils.py +29 -0
- reboost/math/__init__.py +0 -0
- reboost/math/functions.py +175 -0
- reboost/math/stats.py +119 -0
- reboost/optmap/__init__.py +5 -0
- reboost/optmap/cli.py +246 -0
- reboost/optmap/convolve.py +325 -0
- reboost/optmap/create.py +423 -0
- reboost/optmap/evt.py +141 -0
- reboost/optmap/mapview.py +208 -0
- reboost/optmap/numba_pdg.py +26 -0
- reboost/optmap/optmap.py +328 -0
- reboost/profile.py +82 -0
- reboost/shape/__init__.py +0 -0
- reboost/shape/cluster.py +260 -0
- reboost/shape/group.py +189 -0
- reboost/shape/reduction.py +0 -0
- reboost/spms/__init__.py +5 -0
- reboost/spms/pe.py +178 -0
- reboost/units.py +107 -0
- reboost/utils.py +503 -0
- reboost-0.8.3.dist-info/METADATA +82 -0
- reboost-0.8.3.dist-info/RECORD +42 -0
- reboost-0.8.3.dist-info/WHEEL +5 -0
- reboost-0.8.3.dist-info/entry_points.txt +3 -0
- reboost-0.8.3.dist-info/licenses/LICENSE +674 -0
- reboost-0.8.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import awkward as ak
|
|
6
|
+
import numpy as np
|
|
7
|
+
from lgdo import Array, VectorOfVectors
|
|
8
|
+
from lgdo.types import LGDO
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def piecewise_linear_activeness(
|
|
14
|
+
distances: VectorOfVectors | ak.Array, fccd: float, dlf: float
|
|
15
|
+
) -> VectorOfVectors | Array:
|
|
16
|
+
r"""Piecewise linear HPGe activeness model.
|
|
17
|
+
|
|
18
|
+
Based on:
|
|
19
|
+
|
|
20
|
+
.. math::
|
|
21
|
+
|
|
22
|
+
f(d) =
|
|
23
|
+
\begin{cases}
|
|
24
|
+
0 & \text{if } d < f*l, \\
|
|
25
|
+
\frac{x-f*l}{f - f*l} & \text{if } t \leq d < f, \\
|
|
26
|
+
1 & \text{otherwise.}
|
|
27
|
+
\end{cases}
|
|
28
|
+
|
|
29
|
+
Where:
|
|
30
|
+
|
|
31
|
+
- `d`: Distance to surface,
|
|
32
|
+
- `l`: Dead layer fraction, the fraction of the FCCD which is fully inactive
|
|
33
|
+
- `f`: Full charge collection depth (FCCD).
|
|
34
|
+
|
|
35
|
+
In addition, any distance of `np.nan` (for example if the calculation
|
|
36
|
+
was not performed for some steps) is assigned an activeness of one.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
distances
|
|
41
|
+
the distance from each step to the detector surface. Can be either a
|
|
42
|
+
`numpy` or `awkward` array, or a LGDO `VectorOfVectors` or `Array`. The computation
|
|
43
|
+
is performed for each element and the shape preserved in the output.
|
|
44
|
+
|
|
45
|
+
fccd
|
|
46
|
+
the value of the FCCD
|
|
47
|
+
dlf
|
|
48
|
+
the fraction of the FCCD which is fully inactive.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
a :class:`VectorOfVectors` or :class:`Array` of the activeness
|
|
53
|
+
"""
|
|
54
|
+
# convert to ak
|
|
55
|
+
if isinstance(distances, LGDO):
|
|
56
|
+
distances_ak = distances.view_as("ak")
|
|
57
|
+
elif not isinstance(distances, ak.Array):
|
|
58
|
+
distances_ak = ak.Array(distances)
|
|
59
|
+
else:
|
|
60
|
+
distances_ak = distances
|
|
61
|
+
|
|
62
|
+
dl = fccd * dlf
|
|
63
|
+
distances_flat = (
|
|
64
|
+
ak.flatten(distances_ak).to_numpy() if distances_ak.ndim > 1 else distances_ak.to_numpy()
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# compute the linear piecewise
|
|
68
|
+
results = np.full_like(distances_flat, np.nan, dtype=np.float64)
|
|
69
|
+
lengths = ak.num(distances_ak) if distances_ak.ndim > 1 else len(distances_ak)
|
|
70
|
+
|
|
71
|
+
mask1 = (distances_flat > fccd) | np.isnan(distances_flat)
|
|
72
|
+
mask2 = (distances_flat <= dl) & (~mask1)
|
|
73
|
+
mask3 = ~(mask1 | mask2)
|
|
74
|
+
|
|
75
|
+
# assign the values
|
|
76
|
+
results[mask1] = 1
|
|
77
|
+
results[mask2] = 0
|
|
78
|
+
results[mask3] = (distances_flat[mask3] - dl) / (fccd - dl)
|
|
79
|
+
|
|
80
|
+
# reshape
|
|
81
|
+
results = ak.unflatten(ak.Array(results), lengths) if distances_ak.ndim > 1 else results
|
|
82
|
+
|
|
83
|
+
return VectorOfVectors(results) if results.ndim > 1 else Array(results)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def vectorised_active_energy(
|
|
87
|
+
distances: VectorOfVectors | ak.Array,
|
|
88
|
+
edep: VectorOfVectors | ak.Array,
|
|
89
|
+
fccd: float | list,
|
|
90
|
+
dlf: float | list,
|
|
91
|
+
) -> VectorOfVectors | Array:
|
|
92
|
+
r"""Energy after piecewise linear HPGe activeness model vectorised over FCCD or dead layer fraction.
|
|
93
|
+
|
|
94
|
+
Based on the same linear activeness function as :func:`piecewise_linear_activeness`. However,
|
|
95
|
+
this function vectorises the calculation to provide a range of output energies varying the fccd or
|
|
96
|
+
dead layer fraction. Either fccd or dlf can be a list. This adds an extra dimension to the
|
|
97
|
+
output, with the same length as the input fccd or dlf list.
|
|
98
|
+
|
|
99
|
+
.. warning:
|
|
100
|
+
It is not currently implemented to vary both dlf and fccd.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
distances
|
|
105
|
+
the distance from each step to the detector surface. Can be either a
|
|
106
|
+
`awkward` array, or a LGDO `VectorOfVectors` . The computation
|
|
107
|
+
is performed for each element and the first dimension is preserved, a
|
|
108
|
+
new dimension is added vectorising over the FCCD or DLF.
|
|
109
|
+
edep
|
|
110
|
+
the energy for each step.
|
|
111
|
+
fccd
|
|
112
|
+
the value of the FCCD, can be a list.
|
|
113
|
+
dlf
|
|
114
|
+
the fraction of the FCCD which is fully inactive, can be a list.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
a :class:`VectorOfVectors` or :class:`Array` of the activeness
|
|
119
|
+
"""
|
|
120
|
+
# add checks on fccd, dlf
|
|
121
|
+
fccd = np.array(fccd)
|
|
122
|
+
dlf = np.array(dlf)
|
|
123
|
+
|
|
124
|
+
if (fccd.ndim + dlf.ndim) > 1:
|
|
125
|
+
msg = "Currently only one of FCCD and dlf can be varied"
|
|
126
|
+
raise NotImplementedError(msg)
|
|
127
|
+
|
|
128
|
+
# convert fccd and or dlf to the right shape
|
|
129
|
+
if fccd.ndim == 0:
|
|
130
|
+
if dlf.ndim == 0:
|
|
131
|
+
dlf = dlf[np.newaxis]
|
|
132
|
+
fccd = np.full_like(dlf, fccd)
|
|
133
|
+
|
|
134
|
+
dl = fccd * dlf
|
|
135
|
+
|
|
136
|
+
def _convert(field):
|
|
137
|
+
# convert to ak
|
|
138
|
+
if isinstance(field, VectorOfVectors):
|
|
139
|
+
field_ak = field.view_as("ak")
|
|
140
|
+
elif not isinstance(field, ak.Array):
|
|
141
|
+
field_ak = ak.Array(field)
|
|
142
|
+
else:
|
|
143
|
+
msg = f"{field} must be an awkward array or VectorOfVectors"
|
|
144
|
+
raise TypeError(msg)
|
|
145
|
+
|
|
146
|
+
return field_ak, ak.flatten(field_ak).to_numpy()[:, np.newaxis]
|
|
147
|
+
|
|
148
|
+
distances_ak, distances_flat = _convert(distances)
|
|
149
|
+
_, edep_flat = _convert(edep)
|
|
150
|
+
runs = ak.num(distances_ak, axis=-1)
|
|
151
|
+
|
|
152
|
+
# vectorise fccd or tl
|
|
153
|
+
|
|
154
|
+
fccd_list = np.tile(fccd, (len(distances_flat), 1))
|
|
155
|
+
dl_list = np.tile(dl, (len(distances_flat), 1))
|
|
156
|
+
distances_shaped = np.tile(distances_flat, (1, len(dl)))
|
|
157
|
+
|
|
158
|
+
# compute the linear piecewise
|
|
159
|
+
results = np.full_like(fccd_list, np.nan, dtype=np.float64)
|
|
160
|
+
|
|
161
|
+
# Masks
|
|
162
|
+
mask1 = (distances_shaped > fccd_list) | np.isnan(distances_shaped)
|
|
163
|
+
mask2 = ((distances_shaped <= dl_list) | (fccd_list == dl_list)) & ~mask1
|
|
164
|
+
mask3 = ~(mask1 | mask2) # Safe, avoids recomputing anything expensive
|
|
165
|
+
|
|
166
|
+
# Assign values
|
|
167
|
+
results[mask1] = 1.0
|
|
168
|
+
results[mask2] = 0.0
|
|
169
|
+
results[mask3] = (distances_shaped[mask3] - dl_list[mask3]) / (
|
|
170
|
+
fccd_list[mask3] - dl_list[mask3]
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
energy = ak.sum(ak.unflatten(results * edep_flat, runs), axis=-2)
|
|
174
|
+
|
|
175
|
+
return VectorOfVectors(energy) if energy.ndim > 1 else Array(energy.to_numpy())
|
reboost/math/stats.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
|
|
6
|
+
import awkward as ak
|
|
7
|
+
import numpy as np
|
|
8
|
+
from lgdo import Array
|
|
9
|
+
from numpy.typing import ArrayLike
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_resolution(
|
|
15
|
+
energies: ak.Array, channels: ak.Array, tcm_tables: dict, reso_pars: dict, reso_func: Callable
|
|
16
|
+
) -> ak.Array:
|
|
17
|
+
"""Get the resolution for each energy.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
energies
|
|
22
|
+
the energies to smear
|
|
23
|
+
channels
|
|
24
|
+
the channel index for each energy
|
|
25
|
+
tcm_tables
|
|
26
|
+
the mapping from indices to channel names.
|
|
27
|
+
reso_pars
|
|
28
|
+
the pars for each channel.
|
|
29
|
+
reso_func
|
|
30
|
+
the function to compute the resolution.
|
|
31
|
+
"""
|
|
32
|
+
n_pars = len(reso_pars[next(iter(reso_pars))])
|
|
33
|
+
|
|
34
|
+
pars_shaped = []
|
|
35
|
+
|
|
36
|
+
for _ in range(n_pars):
|
|
37
|
+
pars_shaped.append(np.zeros(len(ak.flatten(channels))))
|
|
38
|
+
|
|
39
|
+
num = ak.num(channels, axis=-1)
|
|
40
|
+
|
|
41
|
+
for key, value in tcm_tables.items():
|
|
42
|
+
for i in range(n_pars):
|
|
43
|
+
pars_shaped[i][ak.flatten(channels) == value] = reso_pars[key][i]
|
|
44
|
+
|
|
45
|
+
ch_reso = reso_func(ak.flatten(energies), *pars_shaped)
|
|
46
|
+
return ak.unflatten(ch_reso, num)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def apply_energy_resolution(
|
|
50
|
+
energies: ak.Array, channels: ak.Array, tcm_tables: dict, reso_pars: dict, reso_func: Callable
|
|
51
|
+
):
|
|
52
|
+
"""Apply the energy resolution sampling to an array with many channels.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
energies
|
|
57
|
+
the energies to smear
|
|
58
|
+
channels
|
|
59
|
+
the channel index for each energy
|
|
60
|
+
tcm_tables
|
|
61
|
+
the mapping from indices to channel names.
|
|
62
|
+
reso_pars
|
|
63
|
+
the pars for each channel.
|
|
64
|
+
reso_func
|
|
65
|
+
the function to compute the resolution.
|
|
66
|
+
"""
|
|
67
|
+
num = ak.num(channels, axis=-1)
|
|
68
|
+
|
|
69
|
+
ch_reso = get_resolution(energies, channels, tcm_tables, reso_pars, reso_func)
|
|
70
|
+
energies_flat_smear = gaussian_sample(ak.flatten(energies), ak.flatten(ch_reso))
|
|
71
|
+
|
|
72
|
+
return ak.unflatten(energies_flat_smear, num)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def gaussian_sample(mu: ArrayLike, sigma: ArrayLike | float, *, seed: int | None = None) -> Array:
|
|
76
|
+
r"""Generate samples from a gaussian.
|
|
77
|
+
|
|
78
|
+
Based on:
|
|
79
|
+
|
|
80
|
+
.. math::
|
|
81
|
+
|
|
82
|
+
y_i \sim \mathcal{N}(\mu_i,\sigma_i)
|
|
83
|
+
|
|
84
|
+
where $y_i$ is the output, $x_i$ the input (mu) and $\sigma$ is the standard
|
|
85
|
+
deviation for each point.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
mu
|
|
90
|
+
the mean positions to sample from, should be a flat (ArrayLike) object.
|
|
91
|
+
sigma
|
|
92
|
+
the standard deviation for each input value, can also be a single float.
|
|
93
|
+
seed
|
|
94
|
+
the random seed.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
sampled values.
|
|
99
|
+
"""
|
|
100
|
+
# convert inputs
|
|
101
|
+
|
|
102
|
+
if isinstance(mu, Array):
|
|
103
|
+
mu = mu.view_as("np")
|
|
104
|
+
elif isinstance(mu, ak.Array):
|
|
105
|
+
mu = mu.to_numpy()
|
|
106
|
+
elif not isinstance(mu, np.ndarray):
|
|
107
|
+
mu = np.array(mu)
|
|
108
|
+
|
|
109
|
+
# similar for sigma
|
|
110
|
+
if isinstance(sigma, Array):
|
|
111
|
+
sigma = sigma.view_as("np")
|
|
112
|
+
elif isinstance(sigma, ak.Array):
|
|
113
|
+
sigma = sigma.to_numpy()
|
|
114
|
+
elif not isinstance(sigma, float | int | np.ndarray):
|
|
115
|
+
sigma = np.array(sigma)
|
|
116
|
+
|
|
117
|
+
rng = np.random.default_rng(seed=seed) # Create a random number generator
|
|
118
|
+
|
|
119
|
+
return Array(rng.normal(loc=mu, scale=sigma))
|
reboost/optmap/cli.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import dbetto
|
|
7
|
+
|
|
8
|
+
from ..log_utils import setup_log
|
|
9
|
+
from ..utils import _check_input_file, _check_output_file
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def optical_cli() -> None:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="reboost-optical",
|
|
17
|
+
description="%(prog)s command line interface",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--verbose",
|
|
22
|
+
"-v",
|
|
23
|
+
action="count",
|
|
24
|
+
default=0,
|
|
25
|
+
help="""Increase the program verbosity""",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--bufsize",
|
|
30
|
+
action="store",
|
|
31
|
+
type=int,
|
|
32
|
+
default=int(5e6),
|
|
33
|
+
help="""Row count for input table buffering (only used if applicable). default: %(default)e""",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
37
|
+
|
|
38
|
+
# STEP 1a: build map file from evt tier
|
|
39
|
+
map_parser = subparsers.add_parser("createmap", help="build optical map from evt file(s)")
|
|
40
|
+
map_parser.add_argument(
|
|
41
|
+
"--settings",
|
|
42
|
+
action="store",
|
|
43
|
+
help="""Select a config file for binning.""",
|
|
44
|
+
required=True,
|
|
45
|
+
)
|
|
46
|
+
map_parser.add_argument(
|
|
47
|
+
"--detectors",
|
|
48
|
+
help=(
|
|
49
|
+
"file that contains a list of detector ids that will be produced as additional output maps."
|
|
50
|
+
+ "By default, all channels will be included."
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
map_parser_det_group = map_parser.add_mutually_exclusive_group(required=True)
|
|
54
|
+
map_parser_det_group.add_argument(
|
|
55
|
+
"--geom",
|
|
56
|
+
help="GDML geometry file",
|
|
57
|
+
)
|
|
58
|
+
map_parser.add_argument(
|
|
59
|
+
"--n-procs",
|
|
60
|
+
"-N",
|
|
61
|
+
type=int,
|
|
62
|
+
default=1,
|
|
63
|
+
help="number of worker processes to use. default: %(default)e",
|
|
64
|
+
)
|
|
65
|
+
map_parser.add_argument(
|
|
66
|
+
"--check",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="""Check map statistics after creation. default: %(default)s""",
|
|
69
|
+
)
|
|
70
|
+
map_parser.add_argument(
|
|
71
|
+
"input", help="input stp or optmap-evt LH5 file", metavar="INPUT_EVT", nargs="+"
|
|
72
|
+
)
|
|
73
|
+
map_parser.add_argument("output", help="output map LH5 file", metavar="OUTPUT_MAP")
|
|
74
|
+
|
|
75
|
+
# STEP 1b: view maps
|
|
76
|
+
mapview_parser = subparsers.add_parser(
|
|
77
|
+
"viewmap",
|
|
78
|
+
help="view optical map (arrows: navigate slices/axes, 'c': channel selector)",
|
|
79
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
80
|
+
description=(
|
|
81
|
+
"Interactively view optical maps stored in LH5 files.\n\n"
|
|
82
|
+
"Keyboard controls:\n"
|
|
83
|
+
" left/right - previous/next slice along the current axis\n"
|
|
84
|
+
" up/down - switch slicing axis (x, y, z)\n"
|
|
85
|
+
" c - open channel selector overlay to switch detector map\n\n"
|
|
86
|
+
"Display notes:\n"
|
|
87
|
+
" - Cells where no primary photons were simulated are shown in white.\n"
|
|
88
|
+
" - Cells where no photons were detected are shown in grey.\n"
|
|
89
|
+
" - Cells with values above the colormap maximum are shown in red.\n"
|
|
90
|
+
" - Use --hist to choose which histogram to display. 'prob_unc_rel' shows the\n"
|
|
91
|
+
" relative uncertainty prob_unc / prob where defined.\n"
|
|
92
|
+
" - Use --divide to show the ratio of two map files (this/other)."
|
|
93
|
+
),
|
|
94
|
+
epilog=(
|
|
95
|
+
"Examples:\n"
|
|
96
|
+
" reboost-optical viewmap mymap.lh5\n"
|
|
97
|
+
" reboost-optical viewmap mymap.lh5 --channel _1067205\n"
|
|
98
|
+
" reboost-optical viewmap mymap.lh5 --hist prob_unc_rel --min 0 --max 1\n"
|
|
99
|
+
" reboost-optical viewmap mymap.lh5 --divide other.lh5 --title 'Comparison'"
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
mapview_parser.add_argument("input", help="input map LH5 file", metavar="INPUT_MAP")
|
|
103
|
+
mapview_parser.add_argument(
|
|
104
|
+
"--channel",
|
|
105
|
+
action="store",
|
|
106
|
+
default="all",
|
|
107
|
+
help="channel to display ('all' or '_<detid>'). Press 'c' in the viewer to switch. default: %(default)s",
|
|
108
|
+
)
|
|
109
|
+
mapview_parser.add_argument(
|
|
110
|
+
"--hist",
|
|
111
|
+
choices=("_nr_gen", "_nr_det", "prob", "prob_unc", "prob_unc_rel"),
|
|
112
|
+
action="store",
|
|
113
|
+
default="prob",
|
|
114
|
+
help="select optical map histogram to show. default: %(default)s",
|
|
115
|
+
)
|
|
116
|
+
mapview_parser.add_argument(
|
|
117
|
+
"--divide",
|
|
118
|
+
action="store",
|
|
119
|
+
help="divide by another map file before display (ratio). default: none",
|
|
120
|
+
)
|
|
121
|
+
mapview_parser.add_argument(
|
|
122
|
+
"--min",
|
|
123
|
+
default=1e-4,
|
|
124
|
+
type=(lambda s: s if s == "auto" else float(s)),
|
|
125
|
+
help="colormap min value; use 'auto' for automatic scaling. default: %(default)e",
|
|
126
|
+
)
|
|
127
|
+
mapview_parser.add_argument(
|
|
128
|
+
"--max",
|
|
129
|
+
default=1e-2,
|
|
130
|
+
type=(lambda s: s if s == "auto" else float(s)),
|
|
131
|
+
help="colormap max value; use 'auto' for automatic scaling. default: %(default)e",
|
|
132
|
+
)
|
|
133
|
+
mapview_parser.add_argument("--title", help="title of figure. default: stem of filename")
|
|
134
|
+
|
|
135
|
+
# STEP 1c: merge maps
|
|
136
|
+
mapmerge_parser = subparsers.add_parser("mergemap", help="merge optical maps")
|
|
137
|
+
mapmerge_parser.add_argument(
|
|
138
|
+
"input", help="input map LH5 files", metavar="INPUT_MAP", nargs="+"
|
|
139
|
+
)
|
|
140
|
+
mapmerge_parser.add_argument("output", help="output map LH5 file", metavar="OUTPUT_MAP")
|
|
141
|
+
mapmerge_parser.add_argument(
|
|
142
|
+
"--settings",
|
|
143
|
+
action="store",
|
|
144
|
+
help="""Select a config file for binning.""",
|
|
145
|
+
required=True,
|
|
146
|
+
)
|
|
147
|
+
mapmerge_parser.add_argument(
|
|
148
|
+
"--n-procs",
|
|
149
|
+
"-N",
|
|
150
|
+
type=int,
|
|
151
|
+
default=1,
|
|
152
|
+
help="number of worker processes to use. default: %(default)e",
|
|
153
|
+
)
|
|
154
|
+
mapmerge_parser.add_argument(
|
|
155
|
+
"--check",
|
|
156
|
+
action="store_true",
|
|
157
|
+
help="""Check map statistics after creation. default: %(default)s""",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# STEP 1d: check map
|
|
161
|
+
checkmap_parser = subparsers.add_parser("checkmap", help="check optical maps")
|
|
162
|
+
checkmap_parser.add_argument("input", help="input map LH5 file", metavar="INPUT_MAP")
|
|
163
|
+
|
|
164
|
+
# STEP X: rebin maps
|
|
165
|
+
rebin_parser = subparsers.add_parser("rebin", help="rebin optical maps")
|
|
166
|
+
rebin_parser.add_argument("input", help="input map LH5 files", metavar="INPUT_MAP")
|
|
167
|
+
rebin_parser.add_argument("output", help="output map LH5 file", metavar="OUTPUT_MAP")
|
|
168
|
+
rebin_parser.add_argument("--factor", type=int, help="integer scale-down factor")
|
|
169
|
+
|
|
170
|
+
args = parser.parse_args()
|
|
171
|
+
|
|
172
|
+
log_level = (None, logging.INFO, logging.DEBUG)[min(args.verbose, 2)]
|
|
173
|
+
setup_log(log_level)
|
|
174
|
+
|
|
175
|
+
# STEP 1a: build map file from evt tier
|
|
176
|
+
if args.command == "createmap":
|
|
177
|
+
from .create import create_optical_maps
|
|
178
|
+
|
|
179
|
+
_check_input_file(parser, args.input)
|
|
180
|
+
_check_output_file(parser, args.output)
|
|
181
|
+
|
|
182
|
+
# load settings for binning from config file.
|
|
183
|
+
_check_input_file(parser, args.input, "settings")
|
|
184
|
+
settings = dbetto.utils.load_dict(args.settings)
|
|
185
|
+
|
|
186
|
+
chfilter = "*"
|
|
187
|
+
if args.detectors is not None:
|
|
188
|
+
# load detector ids from a JSON/YAML array
|
|
189
|
+
chfilter = dbetto.utils.load_dict(args.detectors)
|
|
190
|
+
|
|
191
|
+
create_optical_maps(
|
|
192
|
+
args.input,
|
|
193
|
+
settings,
|
|
194
|
+
args.bufsize,
|
|
195
|
+
chfilter=chfilter,
|
|
196
|
+
output_lh5_fn=args.output,
|
|
197
|
+
check_after_create=args.check,
|
|
198
|
+
n_procs=args.n_procs,
|
|
199
|
+
geom_fn=args.geom,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# STEP 1b: view maps
|
|
203
|
+
if args.command == "viewmap":
|
|
204
|
+
from .mapview import view_optmap
|
|
205
|
+
|
|
206
|
+
_check_input_file(parser, args.input)
|
|
207
|
+
if args.divide is not None:
|
|
208
|
+
_check_input_file(parser, args.divide)
|
|
209
|
+
view_optmap(
|
|
210
|
+
args.input,
|
|
211
|
+
args.channel,
|
|
212
|
+
args.divide,
|
|
213
|
+
cmap_min=args.min,
|
|
214
|
+
cmap_max=args.max,
|
|
215
|
+
title=args.title,
|
|
216
|
+
histogram_choice=args.hist,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# STEP 1c: merge maps
|
|
220
|
+
if args.command == "mergemap":
|
|
221
|
+
from .create import merge_optical_maps
|
|
222
|
+
|
|
223
|
+
# load settings for binning from config file.
|
|
224
|
+
_check_input_file(parser, args.input, "settings")
|
|
225
|
+
settings = dbetto.utils.load_dict(args.settings)
|
|
226
|
+
|
|
227
|
+
_check_input_file(parser, args.input)
|
|
228
|
+
_check_output_file(parser, args.output)
|
|
229
|
+
merge_optical_maps(
|
|
230
|
+
args.input, args.output, settings, check_after_create=args.check, n_procs=args.n_procs
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# STEP 1d: check maps
|
|
234
|
+
if args.command == "checkmap":
|
|
235
|
+
from .create import check_optical_map
|
|
236
|
+
|
|
237
|
+
_check_input_file(parser, args.input)
|
|
238
|
+
check_optical_map(args.input)
|
|
239
|
+
|
|
240
|
+
# STEP X: rebin maps
|
|
241
|
+
if args.command == "rebin":
|
|
242
|
+
from .create import rebin_optical_maps
|
|
243
|
+
|
|
244
|
+
_check_input_file(parser, args.input)
|
|
245
|
+
_check_output_file(parser, args.output)
|
|
246
|
+
rebin_optical_maps(args.input, args.output, args.factor)
|