Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross talk correction code for build_evt() #572

Merged
merged 88 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
46a7d87
function to implement the cross talk corrected on awkward arrays
tdixon97 Mar 17, 2024
5b9e67c
fix the checks
tdixon97 Mar 17, 2024
100c0fb
add tests
tdixon97 Mar 17, 2024
f02dd79
updating tests for cxt
tdixon97 Apr 19, 2024
0b65c6f
Merge branch 'legend-exp:main' into main
tdixon97 Apr 19, 2024
ee15b39
updated geds module with cross talk code
tdixon97 Apr 22, 2024
66572ce
improve the description
tdixon97 Apr 22, 2024
1ae805e
style: pre-commit fixes
pre-commit-ci[bot] Apr 22, 2024
92c0833
update to tests
tdixon97 Apr 22, 2024
fc5facc
style fixes
tdixon97 Apr 22, 2024
3838c58
style: pre-commit fixes
pre-commit-ci[bot] Apr 22, 2024
faf53f5
options to read positive ctx matrix
tdixon97 Apr 22, 2024
37bebc3
option to handle also positive cross talk
tdixon97 Apr 22, 2024
d7bf63c
option for positive xtalk
tdixon97 Apr 22, 2024
f17ffb3
style: pre-commit fixes
pre-commit-ci[bot] Apr 22, 2024
107a34b
update names to refer to xtalk not cross_talk
tdixon97 Apr 23, 2024
5fcb198
update to tests to use xtalk not cross_talk
tdixon97 Apr 23, 2024
d24a059
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 Apr 23, 2024
80a4e36
style: pre-commit fixes
pre-commit-ci[bot] Apr 23, 2024
4dfcc7b
pre-commit style fixes
tdixon97 Apr 23, 2024
06ec99c
option to read lh5 files
tdixon97 Apr 23, 2024
d407c1f
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 Apr 23, 2024
33974f1
style: pre-commit fixes
pre-commit-ci[bot] Apr 23, 2024
e25e71b
fix spelling of awkward
tdixon97 Apr 23, 2024
d5af2a8
adding a function for a matrix based xtalk correction
tdixon97 Apr 23, 2024
6a75ef4
fixing a typo in awkward
tdixon97 Apr 23, 2024
b5e7c26
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 Apr 23, 2024
74e5ae1
style: pre-commit fixes
pre-commit-ci[bot] Apr 23, 2024
db26b05
simplification of code + building directly energy array
tdixon97 Apr 26, 2024
fe9f45f
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 Apr 26, 2024
ec81281
removing some unneeded unit tests
tdixon97 Apr 26, 2024
83877e6
update default filenames
tdixon97 May 1, 2024
7e4ec6c
style: pre-commit fixes
pre-commit-ci[bot] May 1, 2024
dc5a889
Merge branch 'legend-exp:main' into main
tdixon97 May 1, 2024
169de3c
update to tests
tdixon97 May 1, 2024
40cbcd3
Merge branch 'main' of github.com:tdixon97/pygama
tdixon97 May 1, 2024
dc3c1d9
style: pre-commit fixes
pre-commit-ci[bot] May 1, 2024
ee301c1
add xtalk calibration code
ggmarshall May 3, 2024
a323eba
Merge pull request #1 from ggmarshall/xtalk
tdixon97 May 3, 2024
34dfc59
style: pre-commit fixes
pre-commit-ci[bot] May 3, 2024
914ecbe
change calibrated to call other func
ggmarshall May 3, 2024
8bcfa13
Merge pull request #2 from ggmarshall/xtalk
tdixon97 May 3, 2024
f18081b
move remove_ops to build_hit
ggmarshall May 3, 2024
7c40029
cleanup and modularise
ggmarshall May 3, 2024
b91c37c
function to filter hits
tdixon97 May 3, 2024
a2f6c1e
style: pre-commit fixes
pre-commit-ci[bot] May 3, 2024
f4bbbe0
cleanup filter_hist, add module importing and add multiplicity_logic …
ggmarshall May 4, 2024
5c46392
fix import
ggmarshall May 4, 2024
b3b03fc
Merge pull request #3 from ggmarshall/xtalk
tdixon97 May 4, 2024
9626f70
cleanup and bugfixes
ggmarshall May 4, 2024
9d92585
Merge pull request #4 from ggmarshall/xtalk
tdixon97 May 4, 2024
ecb679d
support an expression for the energy name and remove not working test
tdixon97 May 4, 2024
495d215
style fixes
tdixon97 May 4, 2024
57d26fb
remove uneeded import code
ggmarshall May 4, 2024
a8f2ac6
add legendmeta
ggmarshall May 4, 2024
367d18f
fix qa
ggmarshall May 4, 2024
f8cb29d
Merge pull request #5 from ggmarshall/xtalk
tdixon97 May 5, 2024
5224ced
fix to name of table column
tdixon97 May 5, 2024
fd9cced
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 May 5, 2024
a4417a8
style: pre-commit fixes
pre-commit-ci[bot] May 5, 2024
74cb5a5
fix legendmeta import
tdixon97 May 5, 2024
e7083a7
style fixes
tdixon97 May 5, 2024
e992543
Merge branch 'main' of https://github.com/tdixon97/pygama
tdixon97 May 5, 2024
40eec51
fix of edge cases
tdixon97 May 6, 2024
042217a
style: pre-commit fixes
pre-commit-ci[bot] May 6, 2024
f0eb5d2
mostly style updates
tdixon97 May 6, 2024
f97340b
Merge branch 'main' of github.com:tdixon97/pygama
tdixon97 May 6, 2024
39b2cdc
remove uneeded lh5 store
tdixon97 May 6, 2024
4b85c28
renaming more functions
tdixon97 May 6, 2024
937e905
ensure hits below threshold are still corrected
tdixon97 May 6, 2024
a9eb000
update to tests
tdixon97 May 6, 2024
d1c981b
style: pre-commit fixes
pre-commit-ci[bot] May 6, 2024
1142384
add docstrings
ggmarshall May 7, 2024
dc81484
fix shape of outputs
tdixon97 May 7, 2024
516efc2
Merge branch 'main' of github.com:tdixon97/pygama
tdixon97 May 7, 2024
c5b2e25
style: pre-commit fixes
pre-commit-ci[bot] May 7, 2024
a3c779e
Merge branch 'main' of https://github.com/tdixon97/pygama into xtalk
ggmarshall May 7, 2024
75451ce
Merge pull request #6 from ggmarshall/xtalk
tdixon97 May 7, 2024
aea1f1d
fix indexing for evt vs hit tables
ggmarshall May 7, 2024
1c6fcd2
fix get_at_channel_vov to preserve ordering and also check that chann…
ggmarshall May 7, 2024
b3e9973
at channels to keep_at evals
ggmarshall May 7, 2024
ff93678
Merge pull request #7 from ggmarshall/xtalk
tdixon97 May 7, 2024
72b1c86
fix default value
ggmarshall May 7, 2024
eb98277
fix configs
ggmarshall May 7, 2024
8587e29
Merge pull request #8 from ggmarshall/xtalk
tdixon97 May 7, 2024
153e7c9
Tidy up/fix docstrings
gipert May 8, 2024
645292f
Restore function removed by mistake
gipert May 8, 2024
18c32c3
Add Tobay to the author list
gipert May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ authors:
- family-names: Marshall
given-names: George
orcid: https://orcid.org/0000-0002-5470-5132
- family-names: Dixon
given-names: Toby
orcid: https://orcid.org/0000-0001-8787-6336
- family-names: D'Andrea
given-names: Valerio
orcid: https://orcid.org/0000-0003-2037-4133
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies = [
"iminuit",
"legend-daq2lh5>=1.2.1",
"legend-pydataobj>=1.6",
"pylegendmeta>=0.9",
"matplotlib",
"numba!=0.53.*,!=0.54.*,!=0.57",
"numpy>=1.21",
Expand Down
51 changes: 30 additions & 21 deletions src/pygama/evt/aggregators.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ def evaluate_to_scalar(
def evaluate_at_channel(
datainfo,
tcm,
channels,
channels_skip,
expr,
field_list,
Expand All @@ -253,6 +254,8 @@ def evaluate_at_channel(
input and output LH5 datainfo with HDF5 groups where tables are found.
tcm
TCM data arrays in an object that can be accessed by attribute.
channels
list of channels to be included for evaluation.
channels_skip
list of channels to be skipped from evaluation and set to default value.
expr
Expand Down Expand Up @@ -281,7 +284,7 @@ def evaluate_at_channel(
evt_ids_ch = np.searchsorted(
tcm.cumulative_length, np.where(tcm.id == ch)[0], "right"
)
if table_name not in channels_skip:
if (table_name in channels) and (table_name not in channels_skip):
res = utils.get_data_at_channel(
datainfo=datainfo,
ch=table_name,
Expand All @@ -307,6 +310,7 @@ def evaluate_at_channel_vov(
expr,
field_list,
ch_comp,
channels,
channels_skip,
pars_dict=None,
default_value=np.nan,
Expand All @@ -326,6 +330,8 @@ def evaluate_at_channel_vov(
list of `dsp/hit/evt` parameter tuples in expression ``(tier, field)``.
ch_comp
array of "rawid"s at which the expression is evaluated.
channels
list of channels to be included for evaluation.
channels_skip
list of channels to be skipped from evaluation and set to default value.
pars_dict
Expand All @@ -335,20 +341,19 @@ def evaluate_at_channel_vov(
"""
f = utils.make_files_config(datainfo)

# blow up vov to aoesa
out = ak.Array([[] for _ in range(len(ch_comp))])
ch_comp_channels = np.unique(ch_comp.flattened_data.nda).astype(int)

channels = np.unique(ch_comp.flattened_data.nda).astype(int)
ch_comp = ch_comp.view_as("ak")
out = np.full(
len(ch_comp.flattened_data.nda), default_value, dtype=type(default_value)
)

type_name = None
for ch in channels:
for ch in ch_comp_channels:
table_name = utils.get_table_name_by_pattern(f.hit.table_fmt, ch)

evt_ids_ch = np.searchsorted(
tcm.cumulative_length, np.where(tcm.id == ch)[0], "right"
)
if table_name not in channels_skip:
if (table_name in channels) and (table_name not in channels_skip):
res = utils.get_data_at_channel(
datainfo=datainfo,
ch=table_name,
Expand All @@ -357,23 +362,27 @@ def evaluate_at_channel_vov(
field_list=field_list,
pars_dict=pars_dict,
)
else:
idx_ch = tcm.idx[tcm.id == ch]
res = np.full(len(idx_ch), default_value)

# see in which events the current channel is present
mask = ak.to_numpy(ak.any(ch_comp == ch, axis=-1), allow_missing=False)
cv = np.full(len(ch_comp), np.nan)
cv[evt_ids_ch] = res
cv[~mask] = np.nan
cv = ak.drop_none(ak.nan_to_none(ak.Array(cv)[:, None]))
new_evt_ids_ch = np.searchsorted(
ch_comp.cumulative_length,
np.where(ch_comp.flattened_data.nda == ch)[0],
"right",
)
matches = np.isin(evt_ids_ch, new_evt_ids_ch)
out[ch_comp.flattened_data.nda == ch] = res[matches]

out = ak.concatenate((out, cv), axis=-1)
else:
length = len(np.where(ch_comp.flattened_data.nda == ch)[0])
res = np.full(length, default_value)
out[ch_comp.flattened_data.nda == ch] = res

if ch == channels[0]:
if ch == ch_comp_channels[0]:
out = out.astype(res.dtype)
type_name = res.dtype

return types.VectorOfVectors(ak.values_astype(out, type_name))
return types.VectorOfVectors(
flattened_data=types.Array(out, dtype=type_name),
cumulative_length=ch_comp.cumulative_length,
)


def evaluate_to_aoesa(
Expand Down
2 changes: 2 additions & 0 deletions src/pygama/evt/build_evt.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ def evaluate_expression(
return aggregators.evaluate_at_channel(
datainfo=datainfo,
tcm=tcm,
channels=channels,
channels_skip=channels_skip,
expr=expr,
field_list=field_list,
Expand All @@ -512,6 +513,7 @@ def evaluate_expression(
expr=expr,
field_list=field_list,
ch_comp=ch_comp,
channels=channels,
channels_skip=channels_skip,
pars_dict=pars_dict,
default_value=default_value,
Expand Down
180 changes: 163 additions & 17 deletions src/pygama/evt/modules/geds.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

from collections.abc import Sequence

import awkward as ak
import numpy as np
from lgdo import lh5, types

from .. import utils
from . import xtalk


def apply_recovery_cut(
Expand All @@ -26,7 +28,7 @@ def apply_recovery_cut(
is_recovering = is_recovering | np.where(
(
((timestamps.nda - tstamp) < time_window)
& ((timestamps.nda - tstamp) > 0)
& ((timestamps.nda - tstamp) >= 0)
),
True,
False,
Expand All @@ -41,33 +43,177 @@ def apply_xtalk_correction(
tcm: utils.TCMData,
table_names: Sequence[str],
*,
energy_observable: types.VectorOfVectors,
rawids: types.VectorOfVectors,
return_mode: str,
uncal_energy_expr: str,
cal_energy_expr: str,
multiplicity_expr: str,
xtalk_threshold: float = None,
xtalk_matrix_filename: str = "",
xtalk_rawid_obj: str = "xtc/rawid_index",
xtalk_matrix_obj: str = "xtc/xtalk_matrix_negative",
positive_xtalk_matrix_obj: str = "xtc/xtalk_matrix_positive",
) -> types.VectorOfVectors:
"""Applies the cross-talk correction to the energy observable.
The format of `xtalk_matrix_filename` should be currently be a path to a lh5 file.

The correction is applied using matrix algebra for all triggers above the threshold.

Parameters
----------
datainfo, tcm, table_names
positional arguments automatically supplied by :func:`.build_evt`.
return_mode
string which can be either energy to return corrected energy or tcm_index
uncal_energy_expr
expression for the pulse parameter to be gathered for the uncalibrated energy (used for correction),
can be a combination of different fields.
cal_energy_expr
expression for the pulse parameter to be gathered for the calibrated energy, used for the xtalk threshold,
can be a combination of different fields.
xtalk_threshold
threshold used for xtalk correction, hits below this energy will not
be used to correct the other hits.
xtalk_matrix_filename
name of the file containing the xtalk matrices.
xtalk_matrix_obj
name of the lh5 object containing the xtalk matrix
positive_xtalk_matrix_obj
name of the lh5 object containing the positive polarity xtalk matrix
xtalk_rawids_obj
name of the lh5 object containing the name of the rawids
"""

xtalk_matrix_rawids = lh5.read_as(xtalk_rawid_obj, xtalk_matrix_filename, "np")
tcm_index_array = xtalk.build_tcm_index_array(tcm, datainfo, xtalk_matrix_rawids)

energy_corr = xtalk.get_xtalk_correction(
tcm,
datainfo,
uncal_energy_expr,
cal_energy_expr,
xtalk_threshold,
xtalk_matrix_filename,
xtalk_rawid_obj,
xtalk_matrix_obj,
positive_xtalk_matrix_obj,
)

multiplicity_mask = xtalk.filter_hits(
datainfo,
tcm,
multiplicity_expr,
energy_corr,
xtalk_matrix_rawids,
)
energy_corr = ak.from_regular(energy_corr)
multiplicity_mask = ak.from_regular(multiplicity_mask)
tcm_index_array = ak.from_regular(tcm_index_array)

if return_mode == "energy":
return types.VectorOfVectors(energy_corr[multiplicity_mask])
elif return_mode == "tcm_index":
return types.VectorOfVectors(tcm_index_array[multiplicity_mask])
else:
raise ValueError(f"Unknown mode: {return_mode}")


def apply_xtalk_correction_and_calibrate(
datainfo: utils.DataInfo,
tcm: utils.TCMData,
table_names: Sequence[str],
*,
return_mode: str,
uncal_energy_expr: str,
cal_energy_expr: str,
cal_par_files: str | Sequence[str],
multiplicity_expr: str,
xtalk_matrix_filename: str,
xtalk_threshold: float = None,
xtalk_rawid_obj: str = "xtc/rawid_index",
xtalk_matrix_obj: str = "xtc/xtalk_matrix_negative",
positive_xtalk_matrix_obj: str = "xtc/xtalk_matrix_positive",
uncal_var: str = "dsp.cuspEmax",
recal_var: str = "hit.cuspEmax_ctc_cal",
) -> types.VectorOfVectors:
"""Applies the cross-talk correction to the energy observable.

The format of `xtalk_matrix_filename` should be...
The correction is applied using matrix algebra for all triggers above the
xalk threshold.

Parameters
----------
datainfo, tcm, table_names
positional arguments automatically supplied by :func:`.build_evt`.
energy_observable
array of energy values to correct, one event per row. The detector
identifier is stored in `rawids`, which has the same layout.
rawids
array of detector identifiers for each energy in `energy_observable`.
return_mode
string which can be either ``energy`` to return corrected energy or
``tcm_index``.
uncal_energy_expr
expression for the pulse parameter to be gathered for the uncalibrated
energy (used for correction), can be a combination of different fields.
cal_energy_expr
expression for the pulse parameter to be gathered for the calibrated
energy, used for the xtalk threshold, can be a combination of different
fields.
cal_par_files
path to the generated hit tier par-files defining the calibration
curves. Used to recalibrate the data after xtalk correction.
multiplicity_expr:
expression defining the logic used to compute the event multiplicity.
xtalk_threshold
threshold used for xtalk correction, hits below this energy will not be
used to correct the other hits.
xtalk_matrix_filename
name of the file containing the cross-talk matrices.
path to the file containing the xtalk matrices.
xtalk_matrix_obj
name of the lh5 object containing the xtalk matrix.
positive_xtalk_matrix_obj
name of the lh5 object containing the positive polarity xtalk matrix.
xtalk_matrix_rawids
name of the lh5 object containing the name of the rawids.
recal_var
name of the energy variable to use for re-calibration.
"""
# read in xtalk matrices
lh5.read_as("", xtalk_matrix_filename, "ak")

# do the correction
energies_corr = ...
xtalk_matrix_rawids = lh5.read_as(xtalk_rawid_obj, xtalk_matrix_filename, "np")
tcm_index_array = xtalk.build_tcm_index_array(tcm, datainfo, xtalk_matrix_rawids)

# return the result as LGDO
return types.VectorOfVectors(
energies_corr, attrs=utils.copy_lgdo_attrs(energy_observable)
energy_corr = xtalk.get_xtalk_correction(
tcm,
datainfo,
uncal_energy_expr,
cal_energy_expr,
xtalk_threshold,
xtalk_matrix_filename,
xtalk_rawid_obj,
xtalk_matrix_obj,
positive_xtalk_matrix_obj,
)

calibrated_corr = xtalk.calibrate_energy(
datainfo,
tcm,
energy_corr,
xtalk_matrix_rawids,
cal_par_files,
uncal_var,
recal_var,
)

multiplicity_mask = xtalk.filter_hits(
datainfo,
tcm,
multiplicity_expr,
calibrated_corr,
xtalk_matrix_rawids,
)

calibrated_corr = ak.from_regular(calibrated_corr)
multiplicity_mask = ak.from_regular(multiplicity_mask)
tcm_index_array = ak.from_regular(tcm_index_array)

if return_mode == "energy":
return types.VectorOfVectors(calibrated_corr[multiplicity_mask])
elif return_mode == "tcm_index":
return types.VectorOfVectors(tcm_index_array[multiplicity_mask])
else:
raise ValueError(f"Unknown mode: {return_mode}")
Loading