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

OPSIM-893: bugfix/feature. Update IntRounded for cross-platform repeatability. #15

Merged
merged 4 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 16 additions & 8 deletions rubin_scheduler/scheduler/basis_functions/basis_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,12 +874,15 @@

def _calc_value(self, conditions, indx=None):
result = self.result.copy()
valid_airmass = np.isfinite(conditions.airmass)

Check warning on line 877 in rubin_scheduler/scheduler/basis_functions/basis_functions.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/basis_functions/basis_functions.py#L877

Added line #L877 was not covered by tests
good_pix = np.where(
(conditions.airmass >= 1.0)
& (IntRounded(conditions.airmass) < self.max_airmass)
& (IntRounded(np.abs(conditions.az_to_sun)) < IntRounded(np.pi / 2.0))
(conditions.airmass[valid_airmass] >= 1.0)
& (IntRounded(conditions.airmass[valid_airmass]) < self.max_airmass)
& (IntRounded(np.abs(conditions.az_to_sun[valid_airmass])) < IntRounded(np.pi / 2.0))
)
result[valid_airmass][good_pix] = (

Check warning on line 883 in rubin_scheduler/scheduler/basis_functions/basis_functions.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/basis_functions/basis_functions.py#L883

Added line #L883 was not covered by tests
conditions.airmass[valid_airmass][good_pix] / self.max_airmass.initial
)
result[good_pix] = conditions.airmass[good_pix] / self.max_airmass.initial
return result


Expand All @@ -906,12 +909,12 @@
self.gap_min = IntRounded(gap_min / 60.0 / 24.0)
self.gap_max = IntRounded(gap_max / 60.0 / 24.0)
self.npairs = npairs

self.survey_features = {}
# Track the number of pairs that have been taken in a night
self.survey_features["Pair_in_night"] = features.PairInNight(
filtername=filtername, gap_min=gap_min, gap_max=gap_max, nside=nside
)

# When was it last observed
# XXX--since this feature is also in Pair_in_night, I should just access that one!
self.survey_features["Last_observed"] = features.LastObserved(filtername=filtername, nside=nside)
Expand All @@ -920,11 +923,16 @@
result = np.zeros(hp.nside2npix(self.nside), dtype=float)
if indx is None:
indx = np.arange(result.size)
diff = IntRounded(conditions.mjd - self.survey_features["Last_observed"].feature[indx])
diff = conditions.mjd - self.survey_features["Last_observed"].feature[indx]
mask = np.isnan(diff)

Check warning on line 927 in rubin_scheduler/scheduler/basis_functions/basis_functions.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/basis_functions/basis_functions.py#L926-L927

Added lines #L926 - L927 were not covered by tests
# remove NaNs from diff, but save mask so we exclude those values later.
diff[mask] = 0.0
ir_diff = IntRounded(diff)

Check warning on line 930 in rubin_scheduler/scheduler/basis_functions/basis_functions.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/basis_functions/basis_functions.py#L929-L930

Added lines #L929 - L930 were not covered by tests
good = np.where(
(diff >= self.gap_min)
& (diff <= self.gap_max)
(ir_diff >= self.gap_min)
& (ir_diff <= self.gap_max)
& (self.survey_features["Pair_in_night"].feature[indx] < self.npairs)
& (~mask)
)[0]
result[indx[good]] += 1.0
return result
Expand Down
32 changes: 17 additions & 15 deletions rubin_scheduler/scheduler/features/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@
if np.size(good) < 0:
self.feature = observations_array[good[-1]]
else:
self.feature = observations_array[-1]
if len(observations_array) > 0:
self.feature = observations_array[-1]

Check warning on line 269 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L268-L269

Added lines #L268 - L269 were not covered by tests

def add_observation(self, observation, indx=None):
if self.survey_name is not None:
Expand Down Expand Up @@ -693,8 +694,8 @@
self.feature = np.zeros(hp.nside2npix(nside), dtype=float)
self.indx = np.arange(self.feature.size)
self.last_observed = LastObserved(filtername=filtername)
self.gap_min = gap_min / (24.0 * 60) # Days
self.gap_max = gap_max / (24.0 * 60) # Days
self.gap_min = IntRounded(gap_min / (24.0 * 60)) # Days
self.gap_max = IntRounded(gap_max / (24.0 * 60)) # Days

Check warning on line 698 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L697-L698

Added lines #L697 - L698 were not covered by tests
self.night = 0
# Need to keep a full record of times and healpixels observed in a night.
self.mjd_log = []
Expand Down Expand Up @@ -725,21 +726,22 @@
self.mjd_log = []
self.hpid_log = []

# record the mjds and healpixels that were observed
self.mjd_log.extend([np.max(observation["mjd"])] * np.size(indx))
self.hpid_log.extend(list(indx))

# Look for the mjds that could possibly pair with observation
tmin = observation["mjd"] - self.gap_max
tmax = observation["mjd"] - self.gap_min
mjd_log = np.array(self.mjd_log)
left = np.searchsorted(mjd_log, tmin).max()
right = np.searchsorted(mjd_log, tmax, side="right").max()
mjd_diff = IntRounded(observation["mjd"] - np.array(self.mjd_log))

Check warning on line 730 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L730

Added line #L730 was not covered by tests
# normally would use np.searchsorted, but need to use IntRounded
# to be sure we are cross-platform repeatable.
in_range_indx = np.where((mjd_diff > self.gap_min) & (mjd_diff < self.gap_max))[0]

Check warning on line 733 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L733

Added line #L733 was not covered by tests
# Now check if any of the healpixels taken in the time gap
# match the healpixels of the observation.
matches = np.in1d(indx, self.hpid_log[int(left) : int(right)])
# XXX--should think if this is the correct (fastest) order to check things in.
self.feature[np.array(indx)[matches]] += 1
if in_range_indx.size > 0:
left = in_range_indx.min()
right = in_range_indx.max() + 1
matches = np.in1d(indx, self.hpid_log[left:right])
self.feature[np.array(indx)[matches]] += 1

Check warning on line 740 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L736-L740

Added lines #L736 - L740 were not covered by tests

# record the mjds and healpixels that were observed
self.mjd_log.extend([np.max(observation["mjd"])] * np.size(indx))
self.hpid_log.extend(list(indx))

Check warning on line 744 in rubin_scheduler/scheduler/features/features.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/features/features.py#L743-L744

Added lines #L743 - L744 were not covered by tests


class RotatorAngle(BaseSurveyFeature):
Expand Down
13 changes: 7 additions & 6 deletions rubin_scheduler/scheduler/schedulers/core_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,13 @@
)
obs_pa = _approx_altaz2pa(alt, az, self.conditions.site.latitude_rad)
rot_tel_pos_expected = (obs_pa - observation["rotSkyPos"]) % (2.0 * np.pi)
if (IntRounded(rot_tel_pos_expected) > IntRounded(self.rotator_limits[0])) & (
IntRounded(rot_tel_pos_expected) < IntRounded(self.rotator_limits[1])
):
diff = np.abs(self.rotator_limits - rot_tel_pos_expected)
limit_indx = np.min(np.where(diff == np.min(diff))[0])
observation["rotSkyPos"] = (obs_pa - self.rotator_limits[limit_indx]) % (2.0 * np.pi)
if np.isfinite(observation["rotSkyPos"]):
if (IntRounded(rot_tel_pos_expected) > IntRounded(self.rotator_limits[0])) & (

Check warning on line 244 in rubin_scheduler/scheduler/schedulers/core_scheduler.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/schedulers/core_scheduler.py#L243-L244

Added lines #L243 - L244 were not covered by tests
IntRounded(rot_tel_pos_expected) < IntRounded(self.rotator_limits[1])
):
diff = np.abs(self.rotator_limits - rot_tel_pos_expected)
limit_indx = np.min(np.where(diff == np.min(diff))[0])
observation["rotSkyPos"] = (obs_pa - self.rotator_limits[limit_indx]) % (2.0 * np.pi)

Check warning on line 249 in rubin_scheduler/scheduler/schedulers/core_scheduler.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/schedulers/core_scheduler.py#L247-L249

Added lines #L247 - L249 were not covered by tests
return observation

def _fill_queue(self):
Expand Down
5 changes: 5 additions & 0 deletions rubin_scheduler/scheduler/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
Class to help force comparisons be made on scaled up integers,
preventing machine precision issues cross-platforms

Note that casting a NaN to an int can have different behaviours
cross-platforms, so will throw an error if attempted.

Parameters
----------
inval : number-like thing
Expand All @@ -81,6 +84,8 @@
"""

def __init__(self, inval, scale=1e5):
if np.any(~np.isfinite(inval)):
raise ValueError("IntRounded can only take finite values.")

Check warning on line 88 in rubin_scheduler/scheduler/utils/utils.py

View check run for this annotation

Codecov / codecov/patch

rubin_scheduler/scheduler/utils/utils.py#L87-L88

Added lines #L87 - L88 were not covered by tests
self.initial = inval
self.value = np.round(inval * scale).astype(int)
self.scale = scale
Expand Down
Loading