Skip to content

Commit

Permalink
Replaced median of pixel charge in pedestal events by the 95% quantil…
Browse files Browse the repository at this point in the history
…e, which

characterizes better the right-side tail of the distribution (more relevant for
the analysis)
  • Loading branch information
moralejo committed Dec 20, 2024
1 parent 8f80135 commit 3f0749e
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 49 deletions.
96 changes: 50 additions & 46 deletions lstchain/image/cleaning.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,19 @@ def apply_dynamic_cleaning(image, signal_pixels, threshold, fraction):

def find_tailcuts(input_dir, run_number):
"""
This functions uses DL1 files to determine tailcuts which are adequate
for the bulk of the pixels in a given run. It does so simply based on the
median (for the whole camera) of the median pixel charge for pedestal
events.
This function uses DL1 files to determine tailcuts which are adequate
for the bulk of the pixels in a given run. The script also returns the
suggested NSB adjustment needed in the "dark-sky" MC to match the data.
The function uses the median (for the whole camera, excluding outliers)
of the 95% quantile of the pixel charge for pedestal events to deduce the
NSB level. It is good to use a large quantile of the pixel charge
distribution (vs. e.g. using the median) because what matters for having
a relaistic noise simulation is the tail on the right-side, i.e. for
large pulses.
For reasons of stability & simplicity of analysis, we cannot decide the
cleaning levels on a subrun-by-subrun basis. We select values which are
valid for the whole run.
The script also returns the suggested NSB adjustment needed in the
"dark-sky" MC to match the data.
cleaning levels (or the NSB tuning) on a subrun-by-subrun basis. We select
values which are more or less valid for the whole run.
The script will process a subset of the subruns (~10, hardcoded) of the run,
distributed uniformly through it.
Expand Down Expand Up @@ -97,7 +101,7 @@ def find_tailcuts(input_dir, run_number):

# Minimum number of interleaved pedestals in subrun to proceed with
# calculation:
min_number_of_ped_events = 10
min_number_of_ped_events = 100

Check warning on line 104 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L104

Added line #L104 was not covered by tests

# Minimum number of valid pixels to consider the calculation of NSB level
# acceptable:
Expand All @@ -113,7 +117,7 @@ def find_tailcuts(input_dir, run_number):

number_of_pedestals = []
usable_pixels = []
median_ped_median_pix_charge = []
median_ped_qt95_pix_charge = []

Check warning on line 120 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L118-L120

Added lines #L118 - L120 were not covered by tests

for dl1_file in dl1_files:
log.info('\nInput file: %s', dl1_file)

Check warning on line 123 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L122-L123

Added lines #L122 - L123 were not covered by tests
Expand All @@ -123,7 +127,7 @@ def find_tailcuts(input_dir, run_number):
pedestal_mask = event_type_data == EventType.SKY_PEDESTAL.value

Check warning on line 127 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L125-L127

Added lines #L125 - L127 were not covered by tests

num_pedestals = pedestal_mask.sum()
if num_pedestals < min_number_of_ped_events:
if num_pedestals < min_number_of_ped_events:
log.warning(f' Too few interleaved pedestals ('

Check warning on line 131 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L129-L131

Added lines #L129 - L131 were not covered by tests
f'{num_pedestals}) - skipped subrun!')
continue

Check warning on line 133 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L133

Added line #L133 was not covered by tests
Expand All @@ -142,50 +146,50 @@ def find_tailcuts(input_dir, run_number):

charges_data = data_images['image']
charges_pedestals = charges_data[pedestal_mask]

Check warning on line 148 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L147-L148

Added lines #L147 - L148 were not covered by tests
# pixel-wise median charge through the subrun (#pixels):
median_pix_charge = np.nanmedian(charges_pedestals, axis=0)
# pixel-wise 95% quantile of ped pix charge through the subrun
# (#pixels):
qt95_pix_charge = np.nanquantile(charges_pedestals, 0.95, axis=0)

Check warning on line 151 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L151

Added line #L151 was not covered by tests
# ignore pixels with 0 signal:
median_pix_charge = np.where(median_pix_charge > 0,
median_pix_charge, np.nan)
qt95_pix_charge = np.where(qt95_pix_charge > 0, qt95_pix_charge, np.nan)

Check warning on line 153 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L153

Added line #L153 was not covered by tests
# median of medians accross camera:
median_median_pix_charge = np.nanmedian(median_pix_charge)
# mean abs deviation of pixel medians:
median_pix_charge_dev = median_abs_deviation(median_pix_charge,
nan_policy='omit')
median_qt95_pix_charge = np.nanmedian(qt95_pix_charge)

Check warning on line 155 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L155

Added line #L155 was not covered by tests
# mean abs deviation of pixel qt95 values:
qt95_pix_charge_dev = median_abs_deviation(qt95_pix_charge,

Check warning on line 157 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L157

Added line #L157 was not covered by tests
nan_policy='omit')

# Just a cut to remove outliers (pixels):
outliers = (np.abs(median_pix_charge - median_median_pix_charge) /
median_pix_charge_dev) > mad_max
outliers = (np.abs(qt95_pix_charge - median_qt95_pix_charge) /

Check warning on line 161 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L161

Added line #L161 was not covered by tests
qt95_pix_charge_dev) > mad_max

if outliers.sum() > 0:
removed_fraction = outliers.sum() / outliers.size
log.info(f' Removed {removed_fraction:.2%} of pixels (outliers) '

Check warning on line 166 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L164-L166

Added lines #L164 - L166 were not covered by tests
f'from pedestal median calculation')

# Now compute the median (for the whole camera) of the medians (for
# Now compute the median (for the whole camera) of the qt95's (for
# each pixel) of the charges in pedestal events. Use only reliable
# pixels for this, and exclude outliers:
n_valid_pixels = np.isfinite(median_pix_charge[reliable_pixels]).sum()
n_valid_pixels = np.isfinite(qt95_pix_charge[reliable_pixels]).sum()
if n_valid_pixels < min_number_of_valid_pixels:
logging.warning(f' Too few valid pixels ({n_valid_pixels}) for '

Check warning on line 174 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L172-L174

Added lines #L172 - L174 were not covered by tests
f'calculation!')
median_ped_median_pix_charge.append(np.nan)
median_ped_qt95_pix_charge.append(np.nan)

Check warning on line 176 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L176

Added line #L176 was not covered by tests
else:
median_ped_median_pix_charge.append(np.nanmedian(
median_pix_charge[reliable_pixels & ~outliers]))

median_ped_qt95_pix_charge.append(np.nanmedian(qt95_pix_charge[

Check warning on line 178 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L178

Added line #L178 was not covered by tests
reliable_pixels &
~outliers]))
# convert to ndarray:
median_ped_median_pix_charge = np.array(median_ped_median_pix_charge)
median_ped_qt95_pix_charge = np.array(median_ped_qt95_pix_charge)
number_of_pedestals = np.array(number_of_pedestals)

Check warning on line 183 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L182-L183

Added lines #L182 - L183 were not covered by tests

# Now compute the median for all processed subruns, which is more robust
# against e.g. subruns affected by car flashes. We exclude subruns
# which have less than half of the median statistics per subrun.
good_stats = number_of_pedestals > 0.5 * np.median(number_of_pedestals)
qped = np.nanmedian(median_ped_median_pix_charge[good_stats])
qped = np.nanmedian(median_ped_qt95_pix_charge[good_stats])

Check warning on line 189 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L188-L189

Added lines #L188 - L189 were not covered by tests
# Now we also remove outliers (subruns) if any:
qped_dev = median_abs_deviation(median_ped_median_pix_charge[good_stats])
not_outlier = (np.abs(median_ped_median_pix_charge - qped) /
qped_dev = median_abs_deviation(median_ped_qt95_pix_charge[good_stats])
not_outlier = (np.abs(median_ped_qt95_pix_charge - qped) /

Check warning on line 192 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L191-L192

Added lines #L191 - L192 were not covered by tests
qped_dev) < mad_max

if (~good_stats).sum() > 0:
Expand All @@ -198,7 +202,7 @@ def find_tailcuts(input_dir, run_number):
f'calculation')

# recompute with good-statistics and well-behaving runs:
qped = np.nanmedian(median_ped_median_pix_charge[good_stats & not_outlier])
qped = np.nanmedian(median_ped_qt95_pix_charge[good_stats & not_outlier])
log.info(f'\nNumber of subruns used in calculations: '

Check warning on line 206 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L205-L206

Added lines #L205 - L206 were not covered by tests
f'{(good_stats & not_outlier).sum()}')

Expand All @@ -217,12 +221,12 @@ def find_tailcuts(input_dir, run_number):
return qped, additional_nsb_rate, newconfig

Check warning on line 221 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L221

Added line #L221 was not covered by tests


def pic_th(mean_ped):
def pic_th(qt95_ped):
"""
Parameters
----------
mean_ped: `float`
mean pixel charge in pedestal events (for the standard
qt95_ped: `float`
95% quantile of pixel charge in pedestal events (for the standard
LocalPeakWindowSearch algo & settings in lstchain)
Returns
Expand All @@ -231,30 +235,30 @@ def pic_th(mean_ped):
recommended picture threshold for image cleaning (from a table)
"""
mp_edges = [2.23, 2.88, 3.53, 4.19, 4.84]
mp_edges = np.array([5.85, 7.25, 8.75, 10.3, 11.67])
picture_threshold = np.array([8, 10, 12, 14, 16, 18])

Check warning on line 239 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L238-L239

Added lines #L238 - L239 were not covered by tests

if mean_ped >= mp_edges[-1]:
if qt95_ped >= mp_edges[-1]:
return picture_threshold[-1]
return picture_threshold[np.where(mp_edges > mean_ped)[0][0]]
return picture_threshold[np.where(mp_edges > qt95_ped)[0][0]]

Check warning on line 243 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L241-L243

Added lines #L241 - L243 were not covered by tests


def get_nsb(median_ped):
def get_nsb(qt95_ped):
"""
Parameters
----------
median_ped: `float`
median pixel charge in pedestal events
qt95_ped: `float`
95% quantile of pixel charge in pedestal events
Returns
-------
`float`
(from a parabolic parametrization) the recommended additional NSB
(in p.e. / ns) that has to be added to the "dark MC" waveforms in
order to match the data for which the median pedestal charge is
median_ped
order to match the data for which the 95% quantile of pedestal pixel
charge is qt95_ped
"""
params = [1.43121978, 0.31553277, 0.07626393]
return (params[1] * (median_ped - params[0]) +
params[2] * (median_ped - params[0]) ** 2)
params = [3.95147396, 0.12642504, 0.01718627]
return (params[1] * (qt95_ped - params[0]) +

Check warning on line 263 in lstchain/image/cleaning.py

View check run for this annotation

Codecov / codecov/patch

lstchain/image/cleaning.py#L262-L263

Added lines #L262 - L263 were not covered by tests
params[2] * (qt95_ped - params[0]) ** 2)
8 changes: 5 additions & 3 deletions lstchain/scripts/lstchain_find_tailcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

log = logging.getLogger(__name__)


def main():
args = parser.parse_args()
log.setLevel(logging.INFO)
Expand All @@ -67,14 +68,15 @@ def main():
sys.exit(1)

Check warning on line 68 in lstchain/scripts/lstchain_find_tailcuts.py

View check run for this annotation

Codecov / codecov/patch

lstchain/scripts/lstchain_find_tailcuts.py#L66-L68

Added lines #L66 - L68 were not covered by tests

run_id = args.run_number
median_qped, additional_nsb_rate, newconfig = find_tailcuts(input_dir,
run_id)
median_qt95_qped, additional_nsb_rate, newconfig = find_tailcuts(input_dir,

Check warning on line 71 in lstchain/scripts/lstchain_find_tailcuts.py

View check run for this annotation

Codecov / codecov/patch

lstchain/scripts/lstchain_find_tailcuts.py#L70-L71

Added lines #L70 - L71 were not covered by tests
run_id)

json_filename = Path(output_dir, f'dl1ab_Run{run_id:05d}.json')
dump_config({'tailcuts_clean_with_pedestal_threshold': newconfig,

Check warning on line 75 in lstchain/scripts/lstchain_find_tailcuts.py

View check run for this annotation

Codecov / codecov/patch

lstchain/scripts/lstchain_find_tailcuts.py#L74-L75

Added lines #L74 - L75 were not covered by tests
'dynamic_cleaning': get_standard_config()['dynamic_cleaning']},
json_filename, overwrite=True)
log.info(f'\nMedian pedestal charge: {median_qped:.3f} p.e.')
log.info(f'\nMedian of 95% quantile of pedestal charge:'

Check warning on line 78 in lstchain/scripts/lstchain_find_tailcuts.py

View check run for this annotation

Codecov / codecov/patch

lstchain/scripts/lstchain_find_tailcuts.py#L78

Added line #L78 was not covered by tests
f' {median_qt95_qped:.3f} p.e.')
log.info('\nCleaning settings:')
log.info(newconfig)
log.info('\nWritten to:')
Expand Down

0 comments on commit 3f0749e

Please sign in to comment.