-
Notifications
You must be signed in to change notification settings - Fork 4
Add native dead and wrong-polarity BPM error model #44
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ def _rotation_matrix(a): | |
| 'reference_x', 'reference_y'] | ||
|
|
||
| # These fields are initialized to ones (not zeros) — multiplicative corrections | ||
| BPM_FIELDS_TO_INITIALISE_ONES = ['gain_corrections_x', 'gain_corrections_y'] | ||
| BPM_FIELDS_TO_INITIALISE_ONES = ['gain_corrections_x', 'gain_corrections_y', 'polarity_x', 'polarity_y'] | ||
|
|
||
| class BPMSystem(BaseModel, extra='forbid'): | ||
| indices: list[int] = [] | ||
|
|
@@ -42,6 +42,10 @@ class BPMSystem(BaseModel, extra='forbid'): | |
| gain_corrections_x: NPARRAY = np.array([]) | ||
| gain_corrections_y: NPARRAY = np.array([]) | ||
|
|
||
| dead: Optional[NPARRAY] = None | ||
| polarity_x: Optional[NPARRAY] = None | ||
| polarity_y: Optional[NPARRAY] = None | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These can be: |
||
|
|
||
| transmission_threshold: float = 0.4 | ||
|
|
||
| _parent: Optional["SimulatedCommissioning"] = PrivateAttr(default=None) | ||
|
|
@@ -64,9 +68,21 @@ def initialize_empty_arrays(self): | |
| if not len(getattr(self, field)): # array is empty | ||
| setattr(self, field, np.zeros(nbpm, dtype=float)) | ||
| for field in BPM_FIELDS_TO_INITIALISE_ONES: | ||
| if not len(getattr(self, field)): # array is empty | ||
| value = getattr(self, field) | ||
| if value is None or not len(value): # optional/unset or empty array | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need for this if dead, polarity_x, polarity_y is declared as np.array([]) by default |
||
| setattr(self, field, np.ones(nbpm, dtype=float)) | ||
|
|
||
| def _overwrite_dead_bpms(self, fake_x, fake_y, noise_x, noise_y): | ||
| """Overwrite dead-BPM readings with amplified noise, but only where the | ||
| beam actually reached the BPM. Dead BPMs downstream of a beam-loss point | ||
| keep their NaN reading instead of fabricating a non-NaN value that would | ||
| inflate transmission/reach metrics. Modifies fake_x/fake_y in place.""" | ||
| if self.dead is not None and self.dead.any(): | ||
| dead_alive_x = self.dead & ~np.isnan(fake_x) | ||
| dead_alive_y = self.dead & ~np.isnan(fake_y) | ||
| fake_x[dead_alive_x] = noise_x[dead_alive_x] * 10 | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the hard-coded "10" factor here. Maybe it can be declared as one of the fields in the BPMSystem class: |
||
| fake_y[dead_alive_y] = noise_y[dead_alive_y] * 10 | ||
|
|
||
| def update_rot_matrices(self): | ||
| self._rot_matrices = _rotation_matrix(self.rolls) | ||
|
|
||
|
|
@@ -111,10 +127,13 @@ def capture_orbit(self, bba=True, subtract_reference=True, use_design=False) -> | |
| noise_x = self._parent.rng.normal(scale=self.noise_co_x) | ||
| noise_y = self._parent.rng.normal(scale=self.noise_co_y) | ||
|
|
||
| fake_orbit_x = (rotated_orbit[0] - self.offsets_x) * (1 + self.calibration_errors_x) + noise_x | ||
| fake_orbit_y = (rotated_orbit[1] - self.offsets_y) * (1 + self.calibration_errors_y) + noise_y | ||
| pol_x = self.polarity_x if self.polarity_x is not None else 1.0 | ||
| pol_y = self.polarity_y if self.polarity_y is not None else 1.0 | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need for the if-clause, polarity_x, polarity_y will always be initialized. |
||
| fake_orbit_x = (rotated_orbit[0] - self.offsets_x) * (1 + self.calibration_errors_x) * pol_x + noise_x | ||
| fake_orbit_y = (rotated_orbit[1] - self.offsets_y) * (1 + self.calibration_errors_y) * pol_y + noise_y | ||
| fake_orbit_x *= self.gain_corrections_x | ||
| fake_orbit_y *= self.gain_corrections_y | ||
| self._overwrite_dead_bpms(fake_orbit_x, fake_orbit_y, noise_x, noise_y) | ||
|
|
||
| if bba: | ||
| # Apply BBA offsets | ||
|
|
@@ -154,6 +173,8 @@ def capture_injection(self, n_turns=1, bba=True, subtract_reference=True, use_de | |
|
|
||
| fake_trajectory_x_tbt = np.zeros([len(self.indices), n_turns]) | ||
| fake_trajectory_y_tbt = np.zeros([len(self.indices), n_turns]) | ||
| pol_x = self.polarity_x if self.polarity_x is not None else 1.0 | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same: no need for the if-clause, polarity_x, polarity_y will always be initialized. |
||
| pol_y = self.polarity_y if self.polarity_y is not None else 1.0 | ||
|
|
||
| for n in range(n_turns): | ||
| one_trajectory = trajectory[:, :, n] | ||
|
|
@@ -162,10 +183,11 @@ def capture_injection(self, n_turns=1, bba=True, subtract_reference=True, use_de | |
| noise_x = self._parent.rng.normal(scale=self.noise_tbt_x) | ||
| noise_y = self._parent.rng.normal(scale=self.noise_tbt_y) | ||
|
|
||
| fake_trajectory_x = (rotated_trajectory[0] - self.offsets_x) * (1 + self.calibration_errors_x) + noise_x | ||
| fake_trajectory_y = (rotated_trajectory[1] - self.offsets_y) * (1 + self.calibration_errors_y) + noise_y | ||
| fake_trajectory_x = (rotated_trajectory[0] - self.offsets_x) * (1 + self.calibration_errors_x) * pol_x + noise_x | ||
| fake_trajectory_y = (rotated_trajectory[1] - self.offsets_y) * (1 + self.calibration_errors_y) * pol_y + noise_y | ||
| fake_trajectory_x *= self.gain_corrections_x | ||
| fake_trajectory_y *= self.gain_corrections_y | ||
| self._overwrite_dead_bpms(fake_trajectory_x, fake_trajectory_y, noise_x, noise_y) | ||
|
|
||
| if bba: | ||
| # Apply BBA offsets | ||
|
|
@@ -211,6 +233,8 @@ def capture_kick(self, n_turns=1, kick_px=0, kick_py=0, bba=True, subtract_refer | |
|
|
||
| fake_trajectory_x_tbt = np.zeros([len(self.indices), n_turns]) | ||
| fake_trajectory_y_tbt = np.zeros([len(self.indices), n_turns]) | ||
| pol_x = self.polarity_x if self.polarity_x is not None else 1.0 | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same: no need for the if-clause, polarity_x, polarity_y will always be initialized. |
||
| pol_y = self.polarity_y if self.polarity_y is not None else 1.0 | ||
|
|
||
| for n in range(n_turns): | ||
| one_trajectory = trajectory[:, :, n] | ||
|
|
@@ -219,10 +243,11 @@ def capture_kick(self, n_turns=1, kick_px=0, kick_py=0, bba=True, subtract_refer | |
| noise_x = self._parent.rng.normal(scale=self.noise_tbt_x) | ||
| noise_y = self._parent.rng.normal(scale=self.noise_tbt_y) | ||
|
|
||
| fake_trajectory_x = (rotated_trajectory[0] - self.offsets_x) * (1 + self.calibration_errors_x) + noise_x | ||
| fake_trajectory_y = (rotated_trajectory[1] - self.offsets_y) * (1 + self.calibration_errors_y) + noise_y | ||
| fake_trajectory_x = (rotated_trajectory[0] - self.offsets_x) * (1 + self.calibration_errors_x) * pol_x + noise_x | ||
| fake_trajectory_y = (rotated_trajectory[1] - self.offsets_y) * (1 + self.calibration_errors_y) * pol_y + noise_y | ||
| fake_trajectory_x *= self.gain_corrections_x | ||
| fake_trajectory_y *= self.gain_corrections_y | ||
| self._overwrite_dead_bpms(fake_trajectory_x, fake_trajectory_y, noise_x, noise_y) | ||
|
|
||
| if bba: | ||
| # Apply BBA offsets | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dead bpms should not be passed to SC.tuning.bad_bpms . The user should declare the bad bpms themselves through appropriate measurements