Skip to content

add a method to extract directly the waveform from PSL#164

Open
tdixon97 wants to merge 17 commits intomainfrom
waveforms
Open

add a method to extract directly the waveform from PSL#164
tdixon97 wants to merge 17 commits intomainfrom
waveforms

Conversation

@tdixon97
Copy link
Collaborator

@tdixon97 tdixon97 commented Feb 5, 2026

@SalehGiovanna this would allow you to emulate a full (ideal) waveform in python.

This is rather slow and probably not something we will use for post-processing but it might be good for tuning the models.

This is a few orders of magnitude faster than SSD directly but still quite alot slower than maximum_current. This is because it computes the full waveform not only searches the maximum.

Possible enhancements are to consider the phi-dependence, interpolate templates between time-steps etc

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

❌ Patch coverage is 60.63830% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.63%. Comparing base (409f161) to head (df3ff51).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/reboost/hpge/psd.py 38.00% 31 Missing ⚠️
src/reboost/hpge/utils.py 86.36% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #164      +/-   ##
==========================================
+ Coverage   68.98%   69.63%   +0.64%     
==========================================
  Files          33       33              
  Lines        2631     2720      +89     
==========================================
+ Hits         1815     1894      +79     
- Misses        816      826      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a new method to extract full waveforms directly from a pulse shape library (PSL) for HPGe detectors. The implementation enables emulating ideal waveforms in Python for model tuning purposes, though it is slower than the existing maximum_current function since it computes the full waveform rather than just finding the maximum.

Changes:

  • Added waveform_from_pulse_shape_library() function and supporting _get_psl_waveforms_impl() to extract full waveforms from pulse shape libraries
  • Made the t0 field optional in get_hpge_pulse_shape_library() to support pulse shape libraries without explicit t0 values
  • Added test coverage for the new waveform extraction functionality

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/reboost/hpge/psd.py Implements new functions to extract full waveforms from pulse shape library by summing energy-weighted templates
src/reboost/hpge/utils.py Makes t0 field optional in pulse shape library loading with default value of 0 and adds default empty units for waveform field
tests/hpge/test_waveforms.py Adds basic test to verify waveform extraction produces correct summed values

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tdixon97
Copy link
Collaborator Author

tdixon97 commented Feb 6, 2026

@copilot in the pulse shape libraries we typically store two waveforms on two angles 0 and 45 deg. Typically there is a repeating symmetry every 45 deg.
I would like to add an option to pass the phi for every hit to reboost and then it choses the PSL that is closer.

Copy link

Copilot AI commented Feb 6, 2026

@tdixon97 I've opened a new pull request, #165, to work on those changes. Once the pull request is ready, I'll request review from you.

tdixon97 and others added 10 commits February 12, 2026 00:14
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: tdixon97 <56904179+tdixon97@users.noreply.github.com>
Co-authored-by: tdixon97 <56904179+tdixon97@users.noreply.github.com>
Co-authored-by: tdixon97 <56904179+tdixon97@users.noreply.github.com>
…D array

Co-authored-by: tdixon97 <56904179+tdixon97@users.noreply.github.com>
…0_deg)

Co-authored-by: tdixon97 <56904179+tdixon97@users.noreply.github.com>
@tdixon97 tdixon97 requested a review from Copilot February 12, 2026 00:15
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +855 to +856
if has_phi and phi_t is not None:
phi_idx = _get_phi_idx(phi_t[j], phi_grid)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the pulse shape library is 4D (r,z,phi,time) but phi is not provided, this falls through to the else branch and indexes pulse_shape_library[r_idx, z_idx] (shape (phi,time)), which cannot be added into waveforms[i] (shape (time,)) and will raise a broadcasting error. If the intended behavior is to default to the first phi template when phi is omitted (as the tests/docstring suggest), handle has_phi with phi is None by using phi_idx=0 (or explicitly selecting a default phi) instead of the 3D indexing path.

Suggested change
if has_phi and phi_t is not None:
phi_idx = _get_phi_idx(phi_t[j], phi_grid)
if has_phi:
# If phi_t is not provided but the pulse shape library has a phi
# dimension, default to the first phi template (index 0).
if phi_t is not None:
phi_idx = _get_phi_idx(phi_t[j], phi_grid)
else:
phi_idx = 0

Copilot uses AI. Check for mistakes.
Comment on lines +82 to 87
else:
t0 = 0
t0_u = "ns"

t0_u = data["t0"].attrs["units"]
dt = data["dt"].value
dt_u = data["dt"].attrs["units"]
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t0 is optional now, but when it is missing you hardcode t0_u = "ns" and later set tu = t0_u. If the file’s dt units are not ns (e.g. us), times = t0 + ... * dt will be numerically correct but the returned t_units (and implied units for times) will be wrong. Consider setting t0_u = dt_u (or tu = dt_u) when t0 is absent, and keep the unit-consistency check strict.

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +135
if "units" not in data[phi_field].attrs:
data[phi_field].attrs["units"] = ""
wf = np.nan_to_num(
data[phi_field].view_as("np", with_units=True), nan=out_of_bounds_val
)
waveform_list.append(wf.m)

# Stack into 4D array (r, z, phi, time)
waveforms_4d = np.stack(waveform_list, axis=2)
phi_array = np.array(phi_angles)
else:
# No phi-dependent fields, use the base field
if "units" not in data[field].attrs:
data[field].attrs["units"] = ""
wf = np.nan_to_num(data[field].view_as("np", with_units=True), nan=out_of_bounds_val)
waveforms_4d = wf.m
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function mutates the on-disk LGDO object (data[...].attrs["units"] = "") as a fallback when the units attribute is missing. That side effect can be surprising and may not be supported for all LGDO backends. Prefer handling missing units without modifying data (e.g., read without units and treat as dimensionless, or use a local default when interpreting the array).

Suggested change
if "units" not in data[phi_field].attrs:
data[phi_field].attrs["units"] = ""
wf = np.nan_to_num(
data[phi_field].view_as("np", with_units=True), nan=out_of_bounds_val
)
waveform_list.append(wf.m)
# Stack into 4D array (r, z, phi, time)
waveforms_4d = np.stack(waveform_list, axis=2)
phi_array = np.array(phi_angles)
else:
# No phi-dependent fields, use the base field
if "units" not in data[field].attrs:
data[field].attrs["units"] = ""
wf = np.nan_to_num(data[field].view_as("np", with_units=True), nan=out_of_bounds_val)
waveforms_4d = wf.m
if "units" in data[phi_field].attrs:
# Field has units: read as quantity and use magnitudes
wf_q = np.nan_to_num(
data[phi_field].view_as("np", with_units=True),
nan=out_of_bounds_val,
)
waveform_list.append(wf_q.m)
else:
# Field has no units: read as plain numpy array and treat as dimensionless
wf_arr = np.nan_to_num(
data[phi_field].view_as("np", with_units=False),
nan=out_of_bounds_val,
)
waveform_list.append(wf_arr)
# Stack into 4D array (r, z, phi, time)
waveforms_4d = np.stack(waveform_list, axis=2)
phi_array = np.array(phi_angles)
else:
# No phi-dependent fields, use the base field
if "units" in data[field].attrs:
wf_q = np.nan_to_num(
data[field].view_as("np", with_units=True),
nan=out_of_bounds_val,
)
waveforms_4d = wf_q.m
else:
wf_arr = np.nan_to_num(
data[field].view_as("np", with_units=False),
nan=out_of_bounds_val,
)
waveforms_4d = wf_arr

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +110
waveforms = waveform_from_pulse_shape_library(edep, r, z, pulse_shape_library_with_phi)

assert waveforms.to_numpy().shape == (1, 100)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims that omitting phi on a phi-enabled library defaults to the first phi template, but it only asserts the output shape. Add an assertion on the waveform values (e.g., that the result matches the 0° template scaling) so the intended behavior is actually verified.

Copilot uses AI. Check for mistakes.
phi_grid=pulse_shape_library.phi,
)

return units.attach_units(ak.Array(waveforms), "keV")
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns a "current waveform" but attaches units of "keV" unconditionally. Since the template/library waveform amplitude is generally unitless/arbitrary (and maximum_current explicitly returns unitless current), attaching energy units here is misleading and can cause downstream unit-conversion confusion. Consider returning an array without units (or, if waveform units are meaningful, store and propagate the waveform units from the library).

Suggested change
return units.attach_units(ak.Array(waveforms), "keV")
return ak.Array(waveforms)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants