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

Add Cat-B calibration onsite scripts #1147

Merged
merged 64 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3222b66
first changes for adding cat-B online script
FrancaCassol Jun 22, 2023
c29332a
Add CatA key
FrancaCassol Jul 24, 2023
797b91e
prepare for calibration key change
FrancaCassol Jul 24, 2023
d2c1ad4
Add function to find interleaved files
FrancaCassol Jul 24, 2023
55df1e8
Add trailet to run on few subruns , for debugging
FrancaCassol Jul 24, 2023
c1efdbf
Add CatB default config variable
FrancaCassol Jul 24, 2023
2b7e91f
onsite script for CatB calibration
FrancaCassol Jul 25, 2023
78e49dd
Eliminate double argument and add missing argument
FrancaCassol Jul 25, 2023
9a8e180
improve sintax
FrancaCassol Jul 25, 2023
1d7c33b
Add script to send Cat-B calibration in batch
FrancaCassol Jul 25, 2023
069606b
correct scaling ratio
FrancaCassol Jul 25, 2023
dacf286
remove scaling ratio
FrancaCassol Jul 25, 2023
dc6ddf4
improvements of the scripts
FrancaCassol Jul 25, 2023
2cf8dbb
Merge branch 'main' of github.com:cta-observatory/cta-lstchain into c…
FrancaCassol Aug 30, 2023
a317154
Improve trailet help
FrancaCassol Aug 30, 2023
d66d1b1
Introduce DataCategory class
FrancaCassol Aug 30, 2023
1a4e639
add test for find_calibration_file
FrancaCassol Aug 30, 2023
2c3670f
change argumennt classes in order to overwrite config values
FrancaCassol Aug 30, 2023
47c72d6
correct category type
FrancaCassol Aug 31, 2023
207e802
minor improvements
FrancaCassol Aug 31, 2023
b80bc99
- correct filter search errors
FrancaCassol Sep 4, 2023
ed349cd
Write interleaved in dir DL1/interleaved
FrancaCassol Sep 4, 2023
f297c61
Write interleaved in dir DL1/interleaved
FrancaCassol Sep 4, 2023
b1d8b8b
Keep generic comment
FrancaCassol Sep 4, 2023
dfd4734
check if not simu
FrancaCassol Sep 4, 2023
26200d5
create interleaved output dir in the data tree
FrancaCassol Sep 5, 2023
83d6abe
Add fixture for interleaved r1 file build from pedcal run
maxnoe Sep 5, 2023
ce9d75e
check interleaved production
FrancaCassol Sep 6, 2023
5e50706
Merge branch 'ctaB_online_script' of github.com:cta-observatory/cta-l…
FrancaCassol Sep 6, 2023
4eaba9a
add test for cat-A calibration script
FrancaCassol Sep 8, 2023
f8a24e2
first version to be finalized
FrancaCassol Sep 15, 2023
54ee2a6
minor changes
FrancaCassol Sep 15, 2023
f76cc63
correct format
FrancaCassol Sep 15, 2023
4e272b2
Make test_data path absolute
maxnoe Sep 15, 2023
2cf44da
Remove file accidentaly commited
maxnoe Sep 15, 2023
cc98f78
Fix path in interleaved_r1_file fixture
maxnoe Sep 15, 2023
bdaaee1
correct batch script
FrancaCassol Sep 19, 2023
8bffa63
Merge branch 'ctaB_online_script' of github.com:cta-observatory/cta-l…
FrancaCassol Sep 19, 2023
af6d96c
Put correct default stat
FrancaCassol Sep 19, 2023
b77f61c
correct time correction sign
FrancaCassol Sep 20, 2023
e47afe1
Correct npe cut estimation
FrancaCassol Sep 20, 2023
bdc8a65
minor change
FrancaCassol Sep 20, 2023
9a10672
set maximum of unusable pixels to exclude bad time intervals
FrancaCassol Sep 21, 2023
6b74a64
Add number of unusable pixel
FrancaCassol Oct 3, 2023
48c5955
check dl2 event number
FrancaCassol Oct 3, 2023
1274a12
change default mongo DB url
FrancaCassol Oct 3, 2023
c2c4fb3
specify DB
FrancaCassol Oct 3, 2023
02f4fd1
correct the expected npe std
FrancaCassol Oct 4, 2023
e6d7f73
update test calibration modules
FrancaCassol Oct 4, 2023
9cfce97
Merge branch 'main' of github.com:cta-observatory/cta-lstchain into c…
FrancaCassol Oct 4, 2023
615e46f
Minor improvements
FrancaCassol Oct 4, 2023
1ead293
Add elements to nitpick_ignore
FrancaCassol Oct 4, 2023
7967446
corrent output dir
FrancaCassol Oct 4, 2023
d22f480
Do not write empty dl2 file
FrancaCassol Oct 5, 2023
8d41eb0
Update plot function to write correct units for the two calibration t…
FrancaCassol Oct 6, 2023
d4df4ba
correct if statement
FrancaCassol Oct 10, 2023
5726b64
correct calibration key
FrancaCassol Oct 12, 2023
c7079b8
improve key naming
FrancaCassol Oct 31, 2023
8345bbd
add gti (good time interval) column to calibration table
FrancaCassol Oct 31, 2023
c51353c
change name of data source dependent data frame
FrancaCassol Oct 31, 2023
8b2815e
Add function to select events in GTI
FrancaCassol Oct 31, 2023
30b5226
Sintax corrections
FrancaCassol Nov 2, 2023
4620173
correct name
FrancaCassol Nov 2, 2023
045a5fd
Update lstchain/reco/utils.py
FrancaCassol Nov 3, 2023
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
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@
("py:class", "t.Type"),
("py:class", "Config"),
("py:class", "Unicode"),
("py:class", "StrDict"),
("py:class", "ClassesType")
morcuended marked this conversation as resolved.
Show resolved Hide resolved
]

# The suffix(es) of source filenames.
Expand Down
45 changes: 42 additions & 3 deletions lstchain/calib/camera/calibration_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ class CalibrationCalculator(Component):

hg_lg_ratio = traits.Float(
1.,
help='HG/LG ratio applied if use_scaled_low_gain is True. In case of calibrated data the ratio should be 1.'
help='HG/LG ratio applied if use_scaled_low_gain is True. The ratio is ~1 for calibrated data, ~17.4 for uncalibrated data.'

Copy link
Member

Choose a reason for hiding this comment

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

Can we maybe change this into two options?

The hg_lg_ratio should be fixed (at least for LST), to 17.4.

It only needs to be applied for Cat-A.

So maybe it's simpler to leave that as 17.4 and have a switch calibrated_input or simular, that is set to False by the cat A calibration script but to True by the cat B script?

Or even automatically determined by the input / if an existing calibration file has been given?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We never used this option for the Cat-A, it is instead useful for Cat-B calibration because pixels with low HV (due to high NSB) can not be correctly calibrated with LG. For the status of the code/data now, still in test phase, it seems the me that such a trailet is enough to deal with all cases (by experts). I do not see the necessity of an automatisation at this level (but a test can be eventually added at a higher level).

).tag(config=True)

classes = (
Expand Down Expand Up @@ -214,8 +215,8 @@ def calculate_calibration_coefficients(self, event):
# define unusables on number of estimated pe
npe_deviation = calib_data.n_pe - npe_median[:,np.newaxis]

# cut on the base of pe statistical uncertainty (adding a 7% spread due to different detection QE among PMs)
tot_std = np.sqrt(npe_median + (self.relative_qe_dispersion * npe_median)**2)
# cut on the base of the pe statistical uncertainty over the camera
tot_std = self.expected_npe_std(npe_median, ff_data.n_events)

npe_outliers = (
np.logical_or(npe_deviation < self.npe_median_cut_outliers[0] * tot_std[:,np.newaxis],
Expand Down Expand Up @@ -296,3 +297,41 @@ def output_interleaved_results(self, event):
new_ff = True

return new_ped, new_ff

def expected_npe_std(self, npe_median, n_events):

"""
The expected standard deviation of the estimated npe over the camera is given in principle by

std_pe_mean=std_npe/sqrt((n_events)+ (relative_qe_dispersion*npe)**2)

where the relative_qe_dispersion is mainly due to different detection QE among PMs.

However, due to the systematics correction associated to the B term, a linear and quadratic
noise component must be added, these components depend on the sample statistics per pixel (n_events).
The parameters in this function (linear_noise_params and quadratic_noise_params) have been obtained with
a fit of the std of filter scan taken in date 2023/05/10 and considering
n_events = [1000,2500,5000,7500,10000,30000]

"""
basic_variance = npe_median/n_events + (self.relative_qe_dispersion * npe_median)**2

# [par0_HG, par0_LG],[par1_HG, par1_LG]
linear_noise_params=np.array(([1.79717813 ,1.72458305e+00],[0.0231544, -1.62036639e-03]))

# [par0_HG, par0_LG],[par1_HG, par1_LG]
quadratic_noise_params=np.array(([4.99670969e-04, 0.00142218],[ 2.49034290e-05,0.0001207]))

# function to estimate the added noise components as function of the sample statistcs
def noise_term(n_events, par):
return par[0]/(np.sqrt(n_events))+par[1]

linear_term = noise_term(n_events, linear_noise_params)
quadratic_term = noise_term(n_events, quadratic_noise_params)

added_variance = (linear_term * npe_median)**2 + (quadratic_term * npe_median**2)**2

std = np.sqrt(basic_variance + added_variance)

return std

12 changes: 7 additions & 5 deletions lstchain/calib/camera/pedestals.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def __init__(self, subarray, **kwargs):
self.extractor = ImageExtractor.from_name(
self.charge_product, parent=self, subarray=subarray
)


def _extract_charge(self, event):
"""
Expand All @@ -117,7 +118,7 @@ def _extract_charge(self, event):
event : general event container

"""

# copy the waveform be cause we do not want to change it for the moment
waveforms = np.copy(event.r1.tel[self.tel_id].waveform)

Expand Down Expand Up @@ -155,17 +156,18 @@ def calculate_pedestals(self, event):
Returns: True if the mon.tel[tel_id].pedestal is updated, False otherwise

"""

# initialize the np array at each cycle
waveform = event.r1.tel[self.tel_id].waveform

# re-initialize counter
if self.num_events_seen == self.sample_size:
self.num_events_seen = 0

pixel_mask = event.mon.tel[self.tel_id].pixel_status.hardware_failing_pixels

self.trigger_time = event.trigger.time

if self.num_events_seen == 0:
self.time_start = self.trigger_time
self.setup_sample_buffers(waveform, self.sample_size)
Expand Down Expand Up @@ -231,7 +233,7 @@ def store_results(self, event):

def setup_sample_buffers(self, waveform, sample_size):
"""Initialize sample buffers"""

n_channels = waveform.shape[0]
n_pix = waveform.shape[1]
shape = (sample_size, n_channels, n_pix)
Expand All @@ -257,7 +259,7 @@ def calculate_pedestal_results(self, trace_integral, masked_pixels_of_sample):
trace_integral,
mask=masked_pixels_of_sample
)

# mean and std over the sample per pixel
max_sigma = self.sigma_clipping_max_sigma
pixel_mean, pixel_median, pixel_std = sigma_clipped_stats(
Expand Down
36 changes: 34 additions & 2 deletions lstchain/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@ def observed_dl1_files(temp_dir_observed_files, run_summary_path):
datacheck_file1 = temp_dir_observed_files / "datacheck_dl1_LST-1.Run02008.0000.h5"
dvr_file1 = temp_dir_observed_files / "DVR_settings_LST-1.Run02008.h5"
pixmasks_file1 = temp_dir_observed_files / "Pixel_selection_LST-1.Run02008.0000.h5"

interleaved_file1 = temp_dir_observed_files / "interleaved/interleaved_LST-1.Run02008.0000.h5"

# Second set of files
dl1_output_path2 = temp_dir_observed_files / "dl1_LST-1.Run02008.0100.h5"
muons_file2 = temp_dir_observed_files / "muons_LST-1.Run02008.0100.fits"
datacheck_file2 = temp_dir_observed_files / "datacheck_dl1_LST-1.Run02008.0100.h5"
interleaved_file2 = temp_dir_observed_files / "interleaved/interleaved_LST-1.Run02008.0100.h5"

run_program(
"lstchain_data_r0_to_dl1",
Expand Down Expand Up @@ -198,12 +200,42 @@ def observed_dl1_files(temp_dir_observed_files, run_summary_path):
'datacheck1': datacheck_file1,
'dvr_file1': dvr_file1,
'pixmasks_file1': pixmasks_file1,
'interleaved_file1': interleaved_file1,
'dl1_file2': dl1_output_path2,
'muons2': muons_file2,
'datacheck2': datacheck_file2
'datacheck2': datacheck_file2,
'interleaved_file2': interleaved_file2
}



@pytest.mark.private_data
@pytest.fixture(scope="session")
def interleaved_r1_file(temp_dir_observed_files, run_summary_path):
test_pedcal_run = test_data / 'real/R0/20200218/LST-1.1.Run02006.0000_first50.fits.fz'

run_program(
"lstchain_data_r0_to_dl1",
"-f",
test_pedcal_run,
"-o",
temp_dir_observed_files,
"--pedestal-file",
test_drs4_pedestal_path,
"--calibration-file",
test_calib_path,
"--time-calibration-file",
test_time_calib_path,
"--pointing-file",
test_drive_report,
'--run-summary-path',
run_summary_path,
"--default-trigger-type=tib"
)

return temp_dir_observed_files / "interleaved/interleaved_LST-1.Run02006.0000.h5"


@pytest.fixture(scope="session")
def simulated_dl2_file(temp_dir_simulated_files, simulated_dl1_file, rf_models):
"""
Expand Down
11 changes: 8 additions & 3 deletions lstchain/io/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
dl1_params_tel_mon_ped_key = "/dl1/event/telescope/monitoring/pedestal"
dl1_params_tel_mon_cal_key = "/dl1/event/telescope/monitoring/calibration"
dl1_params_tel_mon_flat_key = "/dl1/event/telescope/monitoring/flatfield"
dl1_mon_tel_CatB_ped_key = "/dl1/monitoring/telescope/catB/pedestal"
dl1_mon_tel_CatB_cal_key = "/dl1/monitoring/telescope/catB/calibration"
dl1_mon_tel_CatB_flat_key = "/dl1/monitoring/telescope/catB/flatfield"

dl1_params_lstcam_key = "/dl1/event/telescope/parameters/LST_LSTCam"
dl1_images_lstcam_key = "/dl1/event/telescope/image/LST_LSTCam"
dl2_params_lstcam_key = "/dl2/event/telescope/parameters/LST_LSTCam"
Expand Down Expand Up @@ -858,6 +862,7 @@ def write_calibration_data(writer, mon_index, mon_event, new_ped=False, new_ff=F
mon_event.flatfield.prefix = ''
mon_event.calibration.prefix = ''
mon_index.prefix = ''
monitoring_table='telescope/monitoring'

# update index
if new_ped:
Expand All @@ -870,20 +875,20 @@ def write_calibration_data(writer, mon_index, mon_event, new_ped=False, new_ff=F
if new_ped:
# write ped container
writer.write(
table_name="telescope/monitoring/pedestal",
table_name=f"{monitoring_table}/pedestal",
containers=[mon_index, mon_event.pedestal],
)

if new_ff:
# write calibration container
writer.write(
table_name="telescope/monitoring/flatfield",
table_name=f"{monitoring_table}/flatfield",
containers=[mon_index, mon_event.flatfield],
)

# write ff container
writer.write(
table_name="telescope/monitoring/calibration",
table_name=f"{table_group}/calibration",
containers=[mon_index, mon_event.calibration],
)

Expand Down
Loading
Loading