Skip to content

Noise calibration pipeline implemented#111

Draft
augustocattafesta wants to merge 11 commits intomainfrom
noise_calibration
Draft

Noise calibration pipeline implemented#111
augustocattafesta wants to merge 11 commits intomainfrom
noise_calibration

Conversation

@augustocattafesta
Copy link
Copy Markdown
Collaborator

No description provided.



class CalibrationMatrixBase:
class CalibrationMatrix:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the new unified class for the calibration. At the moment the two implemented type of calibrations are for noise and gain, but in the future we're planning to add also pedestal calibration.

The two main attributes of the class are matrix and hits, which contain the information about the calibration results. We are thinking about adding new attributes, such as the uncertainty of the results.

return self.matrix[row, col]


class CalibrationMatrixGain(CalibrationMatrixBase):
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The old classes, that contained the info that are now in CalibrationMatrix, have been replaced by new classes that handles only the calibration process.

These classes take a CalibrationMatrix as an argument, and modify its attributes during the calibration process.

Comment thread src/hexsample/cli.py
"""
self._scalar_gain = isinstance(self.readout.gain, (int, float))

def _gain(self, row: np.ndarray, col: np.ndarray) -> np.ndarray:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I have removed the old interface to select the exact value of the gain for a certain pixel/ROI. Now this can be done directly calling CalibrationMatrix(col, row)

Comment thread src/hexsample/pipeline.py Outdated
Comment on lines +37 to +58



if kwargs.get("map_gain_file") is not None:
gain_file = CalibrationMatrixGain.from_hdf5(kwargs.get("map_gain_file"))
kwargs.update({"gain": gain_file.matrix})
gain_matrix = CalibrationMatrix.from_hdf5(kwargs.get("map_gain_file"))
kwargs.update({"gain": gain_matrix})
else:
default_gain = CalibrationMatrix(kwargs["num_cols"], kwargs["num_rows"])
default_gain.set_value(kwargs["gain"])
kwargs.update({"gain": default_gain})
if kwargs.get("map_enc_file") is not None:
enc_matrix = CalibrationMatrix.from_hdf5(kwargs.get("map_enc_file"))
enc_matrix.fill(enc_matrix.mean(), max_hits=0)
kwargs.update({"enc": enc_matrix})

else:
default_enc = CalibrationMatrix(kwargs["num_cols"], kwargs["num_rows"])
default_enc.set_value(kwargs["enc"])
kwargs.update({"enc": default_enc})



Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We need to fix this, now it's completely nonsense. If we want to pass always a calibration file, this can be immediately solved. In the other case, we need to understand how to handle the creation of the matrix with a scalar value.

Comment thread src/hexsample/pipeline.py Outdated
Comment on lines +66 to +95
gain_map = CalibrationMatrixGain.from_hdf5(map_gain_file).matrix
gain_map = CalibrationMatrix.from_hdf5(map_gain_file).matrix
else:
gain_map = None
noise_map_file = kwargs.get("map_enc_file")
if noise_map_file is not None:
noise_map = CalibrationMatrix.from_hdf5(noise_map_file).matrix
else:
noise_map = None
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Same as before: do we always want to use a calibration file? If yes, the if/else clause is not necessary, but we just load the matrix from the path.

Comment thread src/hexsample/readout.py
Comment on lines +99 to +100
enc: "CalibrationMatrix" = None
gain: "CalibrationMatrix" = None
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't like this initialization, any idea on how to solve it?

Comment thread src/hexsample/readout.py
Comment on lines +180 to 209
def digitize(self, pha: np.ndarray,
coords: Union[RegionOfInterest, list[Tuple[int, int]]]) -> np.ndarray:
"""Digitize the actual signal.

Arguments
---------
pha : array_like
The input array of pixel signals to be digitized.
roi : RegionOfInterest, optional
The region of interest to be read out, used to digitize rectangular readout events.
coords : sequence of (col, row) tuples or None, optional
The coordinates of the pixels to be read out, used to digitize circular readout events.
"""
# Note that the array type of the input pha argument is not guaranteed, here.
# Over the course of the calculation the pha is bound to be a float (the noise
# and the gain are floating-point numbere) before it is rounded to the nearest
# integer. In order to take advantage of the automatic type casting that
# numpy implements in multiplication and addition, we use the pha = pha +/*
# over the pha +/*= form.
# See https://stackoverflow.com/questions/38673531
#
# Add the noise.
if self.enc > 0:
pha = pha + rng.generator.normal(0., self.enc, size=pha.shape)
# ... apply the conversion between electrons and ADC counts, using the gain matrix if
# provied, otherwise using the same gain parameter for all the pixels...
if isinstance(self.gain, float):
pha = pha * self.gain
# Create cols and rows arrays from the input coordinates, depending on the readout mode.
# In case of rectangular readout, we have a RegionOfInterest, otherwise we have a list with
# the coordinates of the pixels to be read out.
if isinstance(coords, RegionOfInterest):
cols, rows = coords.readout_slice()
else:
# If we are digitizing circular readout events, create the gain array to apply to
# the pha by accessing the gain matrix at the coordinates of the pixels to be read out.
if coords is not None:
gain_array = np.empty_like(pha, dtype=float)
for i, coord in enumerate(coords):
col, row = coord
gain_array[i] = self.gain[row, col]
pha = pha * gain_array
# If we are digitizing rectangular readout events, use the roi slices to access the
# gain matrix and create the gain array to apply to the pha.
elif roi is not None:
row_slice, col_slice = roi.readout_slice()
gain_array = self.gain[row_slice, col_slice]
pha = pha * gain_array
# ... round to the nearest integer...
cols, rows = np.array(coords).T
# Add the noise
noise = rng.generator.normal(0., scale=self.enc(rows, cols))
pha = pha + noise
# Apply the conversion between electrons and ADC counts
pha = pha * self.gain(rows, cols)
# Round to the nearest integer
pha = np.round(pha).astype(int)
# ... if necessary, add the offset...
# Add the offset
pha += self.offset
# ... zero suppress the thing...
# Zero suppress the thing.
self.zero_suppress(pha, self.zero_sup_threshold)
# ... flatten the array to simulate the serial readout and return the
# Flatten the array to simulate the serial readout and return the
# array as the BEE would have.
return pha.flatten()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This section was modified a few weeks ago to use the gain matrix, but it became too complicated. I just recovered the older versions and to small changes to use the new CalibrationMatrix callable interface.

The only difference with respect to older versions is that now digitize needs a ROI or a set of coordinates related to the PHA array, because we need to access the calibration matrices in the right positions.

Comment thread src/hexsample/tasks.py Outdated
Comment on lines +221 to +222
gain_map: Optional[CalibrationMatrix] = None
noise_map: Optional[CalibrationMatrix] = None
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Same problem as HexagonalReadout attributes, I don't like to initialize these arguments as None, but I don't have any idea.

Comment thread src/hexsample/tasks.py Outdated
Comment on lines +299 to +304
if gain_map is None:
gain_map = CalibrationMatrix(header["num_cols"], header["num_rows"])
gain_map.set_value(header["gain"])
if noise_map is None:
noise_map = CalibrationMatrix(header["num_cols"], header["num_rows"])
noise_map.set_value(header["enc"])
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If we always use a calibration matrix file, these lines can be deleted.

@augustocattafesta
Copy link
Copy Markdown
Collaborator Author

We are converging to the decision of dropping the use of scalar values in the simulation and reconstruction. This can simplify a lot of things

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.

1 participant