__all__ = (
"example_scheduler",
"sched_argparser",
"set_run_info",
"run_sched",
"gen_long_gaps_survey",
"gen_greedy_surveys",
"generate_blobs",
"generate_twi_blobs",
"generate_twilight_near_sun",
"standard_bf",
)
import argparse
import os
import subprocess
import sys
import healpy as hp
import numpy as np
from astropy import units as u
from astropy.coordinates import SkyCoord
from astropy.utils import iers
import rubin_scheduler
import rubin_scheduler.scheduler.basis_functions as bf
import rubin_scheduler.scheduler.detailers as detailers
from rubin_scheduler.scheduler import sim_runner
from rubin_scheduler.scheduler.model_observatory import ModelObservatory
from rubin_scheduler.scheduler.schedulers import CoreScheduler, SimpleFilterSched
from rubin_scheduler.scheduler.surveys import (
BlobSurvey,
GreedySurvey,
LongGapSurvey,
ScriptedSurvey,
gen_roman_off_season,
gen_roman_on_season,
gen_too_surveys,
generate_ddf_scheduled_obs,
)
from rubin_scheduler.scheduler.targetofo import gen_all_events
from rubin_scheduler.scheduler.utils import ConstantFootprint, CurrentAreaMap, make_rolling_footprints
from rubin_scheduler.site_models import Almanac
from rubin_scheduler.utils import DEFAULT_NSIDE, SURVEY_START_MJD, _hpid2_ra_dec
# So things don't fail on hyak
iers.conf.auto_download = False
# XXX--note this line probably shouldn't be in production
iers.conf.auto_max_age = None
[docs]
def example_scheduler(
nside: int = DEFAULT_NSIDE, mjd_start: float = SURVEY_START_MJD, no_too: bool = False
) -> CoreScheduler:
"""Provide an example baseline survey-strategy scheduler.
Parameters
----------
nside : `int`
Nside for the scheduler maps and basis functions.
mjd_start : `float`
Start date for the survey (MJD).
no_too : `bool`
Turn off ToO simulation. Default False.
Returns
-------
scheduler : `rubin_scheduler.scheduler.CoreScheduler`
A scheduler set up as the baseline survey strategy.
"""
parser = sched_argparser()
args = parser.parse_args(args=[])
args.setup_only = True
args.no_too = no_too
args.dbroot = "example_"
args.outDir = "."
args.nside = nside
args.mjd_start = mjd_start
scheduler = gen_scheduler(args)
return scheduler
[docs]
def standard_bf(
nside,
filtername="g",
filtername2="i",
m5_weight=6.0,
footprint_weight=1.5,
slewtime_weight=3.0,
stayfilter_weight=3.0,
template_weight=12.0,
u_template_weight=50.0,
g_template_weight=50.0,
footprints=None,
n_obs_template=None,
season=300.0,
season_start_hour=-4.0,
season_end_hour=2.0,
moon_distance=30.0,
strict=True,
wind_speed_maximum=20.0,
):
"""Generate the standard basis functions that are shared by blob surveys
Parameters
----------
nside : `int`
The HEALpix nside to use. Defaults to DEFAULT_NSIDE
filtername : `str`
The filter name for the first observation. Default "g".
filtername2 : `str`
The filter name for the second in the pair (None if unpaired).
Default "i".
n_obs_template : `dict`
The number of observations to take every season in each filter.
Default None.
season : `float`
The length of season (i.e., how long before templates expire) (days).
Default 300.
season_start_hour : `float`
Hour angle limits to use when gathering templates.
Default -4 (hours)
sesason_end_hour : `float`
Hour angle limits to use when gathering templates.
Default +2 (hours)
moon_distance : `float`
The mask radius to apply around the moon (degrees).
Default 30.
m5_weight : `float`
The weight for the 5-sigma depth difference basis function.
Default 6.0 (unitless)
footprint_weight : `float`
The weight on the survey footprint basis function.
Default 0.3 (unitless)
slewtime_weight : `float`
The weight on the slewtime basis function. Default 3 (unitless).
stayfilter_weight : `float`
The weight on basis function that tries to stay avoid filter changes.
Default 3 (unitless).
template_weight : `float`
The weight to place on getting image templates every season.
Default 12 (unitless).
u_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a little
higher than the standard template_weight kwarg.
Default 24 (unitless)
g_template_weight : `float`
The weight to place on getting image templates in g-band. Since there
are so few g-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 24 (unitless).
Returns
-------
basis_functions_weights : `list`
list of tuple pairs (basis function, weight) that is
(rubin_scheduler.scheduler.BasisFunction object, float)
"""
template_weights = {
"u": u_template_weight,
"g": g_template_weight,
"r": template_weight,
"i": template_weight,
"z": template_weight,
"y": template_weight,
}
bfs = []
if filtername2 is not None:
bfs.append(
(
bf.M5DiffBasisFunction(filtername=filtername, nside=nside),
m5_weight / 2.0,
)
)
bfs.append(
(
bf.M5DiffBasisFunction(filtername=filtername2, nside=nside),
m5_weight / 2.0,
)
)
else:
bfs.append((bf.M5DiffBasisFunction(filtername=filtername, nside=nside), m5_weight))
if filtername2 is not None:
bfs.append(
(
bf.FootprintBasisFunction(
filtername=filtername,
footprint=footprints,
out_of_bounds_val=np.nan,
nside=nside,
),
footprint_weight / 2.0,
)
)
bfs.append(
(
bf.FootprintBasisFunction(
filtername=filtername2,
footprint=footprints,
out_of_bounds_val=np.nan,
nside=nside,
),
footprint_weight / 2.0,
)
)
else:
bfs.append(
(
bf.FootprintBasisFunction(
filtername=filtername,
footprint=footprints,
out_of_bounds_val=np.nan,
nside=nside,
),
footprint_weight,
)
)
bfs.append(
(
bf.SlewtimeBasisFunction(filtername=filtername, nside=nside),
slewtime_weight,
)
)
if strict:
bfs.append((bf.StrictFilterBasisFunction(filtername=filtername), stayfilter_weight))
else:
bfs.append((bf.FilterChangeBasisFunction(filtername=filtername), stayfilter_weight))
if n_obs_template is not None:
if filtername2 is not None:
bfs.append(
(
bf.NObsPerYearBasisFunction(
filtername=filtername,
nside=nside,
footprint=footprints.get_footprint(filtername),
n_obs=n_obs_template[filtername],
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
),
template_weights[filtername] / 2.0,
)
)
bfs.append(
(
bf.NObsPerYearBasisFunction(
filtername=filtername2,
nside=nside,
footprint=footprints.get_footprint(filtername2),
n_obs=n_obs_template[filtername2],
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
),
template_weights[filtername2] / 2.0,
)
)
else:
bfs.append(
(
bf.NObsPerYearBasisFunction(
filtername=filtername,
nside=nside,
footprint=footprints.get_footprint(filtername),
n_obs=n_obs_template[filtername],
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
),
template_weights[filtername],
)
)
# The shared masks
bfs.append(
(
bf.MoonAvoidanceBasisFunction(nside=nside, moon_distance=moon_distance),
0.0,
)
)
bfs.append((bf.AvoidDirectWind(nside=nside, wind_speed_maximum=wind_speed_maximum), 0))
filternames = [fn for fn in [filtername, filtername2] if fn is not None]
bfs.append((bf.FilterLoadedBasisFunction(filternames=filternames), 0))
bfs.append((bf.PlanetMaskBasisFunction(nside=nside), 0.0))
return bfs
def blob_for_long(
nside,
nexp=2,
exptime=29.2,
filter1s=["g"],
filter2s=["i"],
pair_time=33.0,
camera_rot_limits=[-80.0, 80.0],
n_obs_template=None,
season=300.0,
season_start_hour=-4.0,
season_end_hour=2.0,
shadow_minutes=60.0,
max_alt=76.0,
moon_distance=30.0,
ignore_obs=["DD", "twilight_near_sun"],
m5_weight=6.0,
footprint_weight=1.5,
slewtime_weight=3.0,
stayfilter_weight=3.0,
template_weight=12.0,
u_template_weight=50.0,
g_template_weight=50.0,
footprints=None,
u_nexp1=True,
night_pattern=[True, True],
time_after_twi=30.0,
HA_min=12,
HA_max=24 - 3.5,
blob_names=[],
u_exptime=38.0,
scheduled_respect=30.0,
):
"""
Generate surveys that take observations in blobs.
Parameters
----------
nside : `int`
The HEALpix nside to use. Default to DEFAULT_NSIDE.
nexp : `int`
The number of exposures to use in a visit. Default 2.
exptime : `float`
The exposure time to use per visit (seconds).
Default 29.2
filter1s : `list` [`str`]
The filternames for the first filter in a pair.
Default ["g"].
filter2s : `list` of `str`
The filter names for the second in the pair (None if unpaired).
Default ["i"].
pair_time : `float`
The ideal time between pairs (minutes). Default 33.
camera_rot_limits : `list` of `float`
The limits to impose when rotationally dithering the camera (degrees).
Default [-80., 80.].
n_obs_template : `dict`
The number of observations to take every season in each filter.
If None, sets to 3 each. Default None.
season : float
The length of season (i.e., how long before templates expire) (days)
Default 300.
season_start_hour : `float`
Hour angle limits to use when gathering templates.
Default -4 (hours)
sesason_end_hour : `float`
Hour angle limits to use when gathering templates.
Default +2 (hours)
shadow_minutes : `float`
Used to mask regions around zenith (minutes). Default 60.
max_alt : `float`
The maximium altitude to use when masking zenith (degrees).
Default 76.
moon_distance : `float`
The mask radius to apply around the moon (degrees).
Default 30.
ignore_obs : `str` or `list` of `str`
Ignore observations by surveys that include the given substring(s).
Default "DD".
m5_weight : `float`
The weight for the 5-sigma depth difference basis function.
Default 3 (unitless).
footprint_weight : `float`
The weight on the survey footprint basis function.
Default 0.3 (uniteless).
slewtime_weight : `float`
The weight on the slewtime basis function.
Default 3.0 (uniteless).
stayfilter_weight : `float`
The weight on basis function that tries to stay avoid filter changes.
Default 3.0 (uniteless).
template_weight : `float`
The weight to place on getting image templates every season.
Default 12 (uniteless).
u_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 24 (unitless.)
u_nexp1 : `bool`
Add a detailer to make sure the number of expossures
in a visit is always 1 for u observations.
Default True.
"""
BlobSurvey_params = {
"slew_approx": 7.5,
"filter_change_approx": 140.0,
"read_approx": 2.0,
"flush_time": 30.0,
"smoothing_kernel": None,
"nside": nside,
"seed": 42,
"dither": True,
"twilight_scale": True,
}
surveys = []
if n_obs_template is None:
n_obs_template = {"u": 3, "g": 3, "r": 3, "i": 3, "z": 3, "y": 3}
times_needed = [pair_time, pair_time * 2]
for filtername, filtername2 in zip(filter1s, filter2s):
detailer_list = []
detailer_list.append(
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
)
detailer_list.append(detailers.CloseAltDetailer())
detailer_list.append(detailers.FilterNexp(filtername="u", nexp=1, exptime=u_exptime))
# List to hold tuples of (basis_function_object, weight)
bfs = []
bfs.extend(
standard_bf(
nside,
filtername=filtername,
filtername2=filtername2,
m5_weight=m5_weight,
footprint_weight=footprint_weight,
slewtime_weight=slewtime_weight,
stayfilter_weight=stayfilter_weight,
template_weight=template_weight,
u_template_weight=u_template_weight,
g_template_weight=g_template_weight,
footprints=footprints,
n_obs_template=n_obs_template,
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
)
)
# Make sure we respect scheduled observations
bfs.append((bf.TimeToScheduledBasisFunction(time_needed=scheduled_respect), 0))
# Masks, give these 0 weight
bfs.append(
(
bf.AltAzShadowMaskBasisFunction(
nside=nside, shadow_minutes=shadow_minutes, max_alt=max_alt, pad=3.0
),
0.0,
)
)
if filtername2 is None:
time_needed = times_needed[0]
else:
time_needed = times_needed[1]
bfs.append((bf.TimeToTwilightBasisFunction(time_needed=time_needed), 0.0))
bfs.append((bf.NotTwilightBasisFunction(), 0.0))
bfs.append((bf.AfterEveningTwiBasisFunction(time_after=time_after_twi), 0.0))
bfs.append((bf.HaMaskBasisFunction(ha_min=HA_min, ha_max=HA_max, nside=nside), 0.0))
# don't execute every night
bfs.append((bf.NightModuloBasisFunction(night_pattern), 0.0))
# only execute one blob per night
bfs.append((bf.OnceInNightBasisFunction(notes=blob_names), 0))
# unpack the basis functions and weights
weights = [val[1] for val in bfs]
basis_functions = [val[0] for val in bfs]
if filtername2 is None:
survey_name = "blob_long, %s" % filtername
else:
survey_name = "blob_long, %s%s" % (filtername, filtername2)
if filtername2 is not None:
detailer_list.append(detailers.TakeAsPairsDetailer(filtername=filtername2))
if u_nexp1:
detailer_list.append(detailers.FilterNexp(filtername="u", nexp=1))
surveys.append(
BlobSurvey(
basis_functions,
weights,
filtername1=filtername,
filtername2=filtername2,
exptime=exptime,
ideal_pair_time=pair_time,
survey_name=survey_name,
ignore_obs=ignore_obs,
nexp=nexp,
detailers=detailer_list,
**BlobSurvey_params,
)
)
return surveys
[docs]
def gen_long_gaps_survey(
footprints,
nside=DEFAULT_NSIDE,
night_pattern=[True, True],
gap_range=[2, 7],
HA_min=12,
HA_max=24 - 3.5,
time_after_twi=120,
u_template_weight=50.0,
g_template_weight=50.0,
u_exptime=38.0,
nexp=2,
):
"""
Paramterers
-----------
footprints : `rubin_scheduler.scheduler.utils.footprints.Footprints`
The footprints to be used.
night_pattern : `list` [`bool`]
Which nights to let the survey execute. Default of [True, True]
executes every night.
gap_range : `list` of `float`
Range of times to attempt to to gather pairs (hours).
Default [2, 7].
HA_min : `float`
The hour angle limits passed to the initial blob scheduler.
Default 12 (hours)
HA_max : `float`
The hour angle limits passed to the initial blob scheduler.
Default 20.5 (hours).
time_after_twi : `float`
The time after evening twilight to attempt long gaps (minutes).
Default 120.
u_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 50 (unitless.)
g_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 50 (unitless.)
u_exptime : `float`
Exposure time to use for u-band visits (seconds).
Default 38.
nexp : `int`
Number of exposures per visit. Default 2.
"""
surveys = []
f1 = ["g", "r", "i"]
f2 = ["r", "i", "z"]
# Maybe force scripted to not go in twilight?
blob_names = []
for fn1, fn2 in zip(f1, f2):
for ab in ["a", "b"]:
blob_names.append("blob_long, %s%s, %s" % (fn1, fn2, ab))
for filtername1, filtername2 in zip(f1, f2):
blob = blob_for_long(
footprints=footprints,
nside=nside,
filter1s=[filtername1],
filter2s=[filtername2],
night_pattern=night_pattern,
time_after_twi=time_after_twi,
HA_min=HA_min,
HA_max=HA_max,
u_template_weight=u_template_weight,
g_template_weight=g_template_weight,
blob_names=blob_names,
u_exptime=u_exptime,
nexp=nexp,
)
scripted = ScriptedSurvey(
[bf.AvoidDirectWind(nside=nside)],
nside=nside,
ignore_obs=["blob", "DDF", "twi", "pair"],
)
surveys.append(LongGapSurvey(blob[0], scripted, gap_range=gap_range, avoid_zenith=True))
return surveys
[docs]
def gen_greedy_surveys(
nside=DEFAULT_NSIDE,
nexp=2,
exptime=29.2,
filters=["r", "i", "z", "y"],
camera_rot_limits=[-80.0, 80.0],
shadow_minutes=0.0,
max_alt=76.0,
moon_distance=30.0,
ignore_obs=["DD", "twilight_near_sun"],
m5_weight=3.0,
footprint_weight=0.75,
slewtime_weight=3.0,
stayfilter_weight=100.0,
repeat_weight=-1.0,
footprints=None,
):
"""
Make a quick set of greedy surveys
This is a convenience function to generate a list of survey objects
that can be used with
rubin_scheduler.scheduler.schedulers.Core_scheduler.
To ensure we are robust against changes in the sims_featureScheduler
codebase, all kwargs are
explicitly set.
Parameters
----------
nside : `int`
The HEALpix nside to use
nexp : `int`
The number of exposures to use in a visit. Default 1.
exptime : `float`
The exposure time to use per visit (seconds).
Default 29.2
filters : `list` of `str`
Which filters to generate surveys for.
Default ['r', 'i', 'z', 'y'].
camera_rot_limits : `list` of `float`
The limits to impose when rotationally dithering the camera (degrees).
Default [-80., 80.].
shadow_minutes : `float`
Used to mask regions around zenith (minutes).
Default 60.
max_alt : `float`
The maximium altitude to use when masking zenith (degrees).
Default 76
moon_distance : `float`
The mask radius to apply around the moon (degrees).
Default 30.
ignore_obs : `str` or `list` of `str`
Ignore observations by surveys that include the given substring(s).
Default ["DD", "twilight_near_sun"].
m5_weight : `float`
The weight for the 5-sigma depth difference basis function.
Default 3 (unitless).
footprint_weight : `float`
The weight on the survey footprint basis function.
Default 0.3 (uniteless).
slewtime_weight : `float`
The weight on the slewtime basis function.
Default 3.0 (uniteless).
stayfilter_weight : `float`
The weight on basis function that tries to stay avoid filter changes.
Default 3.0 (uniteless).
"""
# Define the extra parameters that are used in the greedy survey. I
# think these are fairly set, so no need to promote to utility func kwargs
greed_survey_params = {
"block_size": 1,
"smoothing_kernel": None,
"seed": 42,
"camera": "LSST",
"dither": True,
"survey_name": "greedy",
}
surveys = []
detailer_list = [
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
]
detailer_list.append(detailers.Rottep2RotspDesiredDetailer())
for filtername in filters:
bfs = []
bfs.extend(
standard_bf(
nside,
filtername=filtername,
filtername2=None,
m5_weight=m5_weight,
footprint_weight=footprint_weight,
slewtime_weight=slewtime_weight,
stayfilter_weight=stayfilter_weight,
template_weight=0,
u_template_weight=0,
g_template_weight=0,
footprints=footprints,
n_obs_template=None,
strict=False,
)
)
bfs.append(
(
bf.VisitRepeatBasisFunction(
gap_min=0, gap_max=2 * 60.0, filtername=None, nside=nside, npairs=20
),
repeat_weight,
)
)
# Masks, give these 0 weight
bfs.append(
(
bf.AltAzShadowMaskBasisFunction(
nside=nside, shadow_minutes=shadow_minutes, max_alt=max_alt, pad=3.0
),
0,
)
)
weights = [val[1] for val in bfs]
basis_functions = [val[0] for val in bfs]
surveys.append(
GreedySurvey(
basis_functions,
weights,
exptime=exptime,
filtername=filtername,
nside=nside,
ignore_obs=ignore_obs,
nexp=nexp,
detailers=detailer_list,
**greed_survey_params,
)
)
return surveys
[docs]
def generate_blobs(
nside,
nexp=2,
exptime=29.2,
filter1s=["u", "u", "g", "r", "i", "z", "y"],
filter2s=["g", "r", "r", "i", "z", "y", "y"],
pair_time=33.0,
camera_rot_limits=[-80.0, 80.0],
n_obs_template=None,
season=300.0,
season_start_hour=-4.0,
season_end_hour=2.0,
shadow_minutes=60.0,
max_alt=76.0,
moon_distance=30.0,
ignore_obs=["DD", "twilight_near_sun"],
m5_weight=6.0,
footprint_weight=1.5,
slewtime_weight=3.0,
stayfilter_weight=3.0,
template_weight=12.0,
u_template_weight=50.0,
g_template_weight=50.0,
footprints=None,
u_nexp1=True,
scheduled_respect=45.0,
good_seeing={"g": 3, "r": 3, "i": 3},
good_seeing_weight=3.0,
mjd_start=1,
repeat_weight=-20,
u_exptime=38.0,
):
"""
Generate surveys that take observations in blobs.
Parameters
----------
nside : `int`
The HEALpix nside to use
nexp : int
The number of exposures to use in a visit.
Default 1.
exptime : `float`
The exposure time to use per visit (seconds).
Default 29.2
filter1s : `list` [`str`]
The filternames for the first set.
Default ["u", "u", "g", "r", "i", "z", "y"]
filter2s : `list` of `str`
The filter names for the second in the pair (None if unpaired)
Default ["g", "r", "r", "i", "z", "y", "y"].
pair_time : `float`
The ideal time between pairs (minutes).
Default 33.
camera_rot_limits : `list` of `float`
The limits to impose when rotationally dithering the camera (degrees).
Default [-80., 80.].
n_obs_template : `dict`
The number of observations to take every season in each filter.
If None, sets to 3 each.
season : `float`
The length of season (i.e., how long before templates expire) (days).
Default 300.
season_start_hour : `float`
Hour angle limits to use when gathering templates.
Default -4 (hours)
sesason_end_hour : `float`
Hour angle limits to use when gathering templates.
Default +2 (hours)
shadow_minutes : `float`
Used to mask regions around zenith (minutes).
Default 60.
max_alt : `float`
The maximium altitude to use when masking zenith (degrees).
Default 76.
moon_distance : `float`
The mask radius to apply around the moon (degrees).
Default 30.
ignore_obs : `str` or `list` of `str`
Ignore observations by surveys that include the given substring(s).
Default ["DD", "twilight_near_sun"].
m5_weight : `float`
The weight for the 5-sigma depth difference basis function.
Default 3 (unitless).
footprint_weight : `float`
The weight on the survey footprint basis function.
Default 0.3 (uniteless).
slewtime_weight : `float`
The weight on the slewtime basis function.
Default 3.0 (uniteless).
stayfilter_weight : `float`
The weight on basis function that tries to stay avoid filter changes.
Default 3.0 (uniteless).
template_weight : `float`
The weight to place on getting image templates every season.
Default 12 (unitless).
u_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 24 (unitless).
u_nexp1 : `bool`
Add a detailer to make sure the number of expossures in a visit
is always 1 for u observations. Default True.
scheduled_respect : `float`
How much time to require there be before a pre-scheduled
observation (minutes). Default 45.
"""
BlobSurvey_params = {
"slew_approx": 7.5,
"filter_change_approx": 140.0,
"read_approx": 2.0,
"flush_time": 30.0,
"smoothing_kernel": None,
"nside": nside,
"seed": 42,
"dither": True,
"twilight_scale": False,
}
if n_obs_template is None:
n_obs_template = {"u": 3, "g": 3, "r": 3, "i": 3, "z": 3, "y": 3}
surveys = []
times_needed = [pair_time, pair_time * 2]
for filtername, filtername2 in zip(filter1s, filter2s):
detailer_list = []
detailer_list.append(
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
)
detailer_list.append(detailers.Rottep2RotspDesiredDetailer())
detailer_list.append(detailers.CloseAltDetailer())
detailer_list.append(detailers.FlushForSchedDetailer())
# List to hold tuples of (basis_function_object, weight)
bfs = []
bfs.extend(
standard_bf(
nside,
filtername=filtername,
filtername2=filtername2,
m5_weight=m5_weight,
footprint_weight=footprint_weight,
slewtime_weight=slewtime_weight,
stayfilter_weight=stayfilter_weight,
template_weight=template_weight,
u_template_weight=u_template_weight,
g_template_weight=g_template_weight,
footprints=footprints,
n_obs_template=n_obs_template,
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
)
)
bfs.append(
(
bf.VisitRepeatBasisFunction(
gap_min=0, gap_max=3 * 60.0, filtername=None, nside=nside, npairs=20
),
repeat_weight,
)
)
# Insert things for getting good seeing templates
if filtername2 is not None:
if filtername in list(good_seeing.keys()):
bfs.append(
(
bf.NGoodSeeingBasisFunction(
filtername=filtername,
nside=nside,
mjd_start=mjd_start,
footprint=footprints.get_footprint(filtername),
n_obs_desired=good_seeing[filtername],
),
good_seeing_weight,
)
)
if filtername2 in list(good_seeing.keys()):
bfs.append(
(
bf.NGoodSeeingBasisFunction(
filtername=filtername2,
nside=nside,
mjd_start=mjd_start,
footprint=footprints.get_footprint(filtername2),
n_obs_desired=good_seeing[filtername2],
),
good_seeing_weight,
)
)
else:
if filtername in list(good_seeing.keys()):
bfs.append(
(
bf.NGoodSeeingBasisFunction(
filtername=filtername,
nside=nside,
mjd_start=mjd_start,
footprint=footprints.get_footprint(filtername),
n_obs_desired=good_seeing[filtername],
),
good_seeing_weight,
)
)
# Make sure we respect scheduled observations
bfs.append((bf.TimeToScheduledBasisFunction(time_needed=scheduled_respect), 0))
# Masks, give these 0 weight
bfs.append(
(
bf.AltAzShadowMaskBasisFunction(
nside=nside, shadow_minutes=shadow_minutes, max_alt=max_alt, pad=3.0
),
0.0,
)
)
if filtername2 is None:
time_needed = times_needed[0]
else:
time_needed = times_needed[1]
bfs.append((bf.TimeToTwilightBasisFunction(time_needed=time_needed), 0.0))
bfs.append((bf.NotTwilightBasisFunction(), 0.0))
# unpack the basis functions and weights
weights = [val[1] for val in bfs]
basis_functions = [val[0] for val in bfs]
if filtername2 is None:
survey_name = "pair_%i, %s" % (pair_time, filtername)
else:
survey_name = "pair_%i, %s%s" % (pair_time, filtername, filtername2)
if filtername2 is not None:
detailer_list.append(detailers.TakeAsPairsDetailer(filtername=filtername2))
if u_nexp1:
detailer_list.append(detailers.FilterNexp(filtername="u", nexp=1, exptime=u_exptime))
surveys.append(
BlobSurvey(
basis_functions,
weights,
filtername1=filtername,
filtername2=filtername2,
exptime=exptime,
ideal_pair_time=pair_time,
survey_name=survey_name,
ignore_obs=ignore_obs,
nexp=nexp,
detailers=detailer_list,
**BlobSurvey_params,
)
)
return surveys
[docs]
def generate_twi_blobs(
nside,
nexp=2,
exptime=29.2,
filter1s=["r", "i", "z", "y"],
filter2s=["i", "z", "y", "y"],
pair_time=15.0,
camera_rot_limits=[-80.0, 80.0],
n_obs_template=None,
season=300.0,
season_start_hour=-4.0,
season_end_hour=2.0,
shadow_minutes=60.0,
max_alt=76.0,
moon_distance=30.0,
ignore_obs=["DD", "twilight_near_sun"],
m5_weight=6.0,
footprint_weight=1.5,
slewtime_weight=3.0,
stayfilter_weight=3.0,
template_weight=12.0,
footprints=None,
repeat_night_weight=None,
wfd_footprint=None,
scheduled_respect=15.0,
repeat_weight=-1.0,
night_pattern=None,
):
"""
Generate surveys that take observations in blobs.
Parameters
----------
nside : `int`
The HEALpix nside to use
nexp : `int`
The number of exposures to use in a visit.
exptime : `float`
The exposure time to use per visit (seconds).
Default 29.2
filter1s : `list` of `str`
The filternames for the first set.
Default ["r", "i", "z", "y"].
filter2s : `list` of `str`
The filter names for the second in the pair (None if unpaired).
Default ["i", "z", "y", "y"].
pair_time : `float`
The ideal time between pairs (minutes). Default 22.
camera_rot_limits : `list` of `float`
The limits to impose when rotationally dithering the camera (degrees).
Default [-80., 80.].
n_obs_template : `dict`
The number of observations to take every season in each filter.
If None, sets to 3 each. Default None.
season : `float`
The length of season (i.e., how long before templates expire) (days).
Default 300.
season_start_hour : `float`
Hour angle limits to use when gathering templates.
Default -4 (hours)
sesason_end_hour : `float`
Hour angle limits to use when gathering templates.
Default +2 (hours)
shadow_minutes : `float`
Used to mask regions around zenith (minutes).
Default 60.
max_alt : `float`
The maximium altitude to use when masking zenith (degrees).
Default 76.
moon_distance : `float`
The mask radius to apply around the moon (degrees).
Default 30.
ignore_obs : `str` or `list` of `str`
Ignore observations by surveys that include the given substring(s).
Default ["DD", "twilight_near_sun"].
m5_weight : `float`
The weight for the 5-sigma depth difference basis function.
Default 3 (unitless).
footprint_weight : `float`
The weight on the survey footprint basis function.
Default 0.3 (uniteless).
slewtime_weight : `float`
The weight on the slewtime basis function.
Default 3.0 (uniteless).
stayfilter_weight : `float`
The weight on basis function that tries to stay avoid filter changes.
Default 3.0 (uniteless).
template_weight : `float`
The weight to place on getting image templates every season.
Default 12 (unitless).
u_template_weight : `float`
The weight to place on getting image templates in u-band. Since there
are so few u-visits, it can be helpful to turn this up a
little higher than the standard template_weight kwarg.
Default 24 (unitless).
"""
BlobSurvey_params = {
"slew_approx": 7.5,
"filter_change_approx": 140.0,
"read_approx": 2.0,
"flush_time": 30.0,
"smoothing_kernel": None,
"nside": nside,
"seed": 42,
"dither": True,
"twilight_scale": False,
"in_twilight": True,
}
surveys = []
if n_obs_template is None:
n_obs_template = {"u": 3, "g": 3, "r": 3, "i": 3, "z": 3, "y": 3}
times_needed = [pair_time, pair_time * 2]
for filtername, filtername2 in zip(filter1s, filter2s):
detailer_list = []
detailer_list.append(
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
)
detailer_list.append(detailers.Rottep2RotspDesiredDetailer())
detailer_list.append(detailers.CloseAltDetailer())
detailer_list.append(detailers.FlushForSchedDetailer())
# List to hold tuples of (basis_function_object, weight)
bfs = []
bfs.extend(
standard_bf(
nside,
filtername=filtername,
filtername2=filtername2,
m5_weight=m5_weight,
footprint_weight=footprint_weight,
slewtime_weight=slewtime_weight,
stayfilter_weight=stayfilter_weight,
template_weight=template_weight,
u_template_weight=0,
g_template_weight=0,
footprints=footprints,
n_obs_template=n_obs_template,
season=season,
season_start_hour=season_start_hour,
season_end_hour=season_end_hour,
)
)
bfs.append(
(
bf.VisitRepeatBasisFunction(
gap_min=0, gap_max=2 * 60.0, filtername=None, nside=nside, npairs=20
),
repeat_weight,
)
)
if repeat_night_weight is not None:
bfs.append(
(
bf.AvoidLongGapsBasisFunction(
nside=nside,
filtername=None,
min_gap=0.0,
max_gap=10.0 / 24.0,
ha_limit=3.5,
footprint=wfd_footprint,
),
repeat_night_weight,
)
)
# Make sure we respect scheduled observations
bfs.append((bf.TimeToScheduledBasisFunction(time_needed=scheduled_respect), 0))
# Masks, give these 0 weight
bfs.append(
(
bf.AltAzShadowMaskBasisFunction(
nside=nside,
shadow_minutes=shadow_minutes,
max_alt=max_alt,
pad=3.0,
),
0.0,
)
)
if filtername2 is None:
time_needed = times_needed[0]
else:
time_needed = times_needed[1]
bfs.append((bf.TimeToTwilightBasisFunction(time_needed=time_needed, alt_limit=12), 0.0))
# Let's turn off twilight blobs on nights where we are
# doing NEO hunts
bfs.append((bf.NightModuloBasisFunction(pattern=night_pattern), 0))
# unpack the basis functions and weights
weights = [val[1] for val in bfs]
basis_functions = [val[0] for val in bfs]
if filtername2 is None:
survey_name = "pair_%i, %s" % (pair_time, filtername)
else:
survey_name = "pair_%i, %s%s" % (pair_time, filtername, filtername2)
if filtername2 is not None:
detailer_list.append(detailers.TakeAsPairsDetailer(filtername=filtername2))
surveys.append(
BlobSurvey(
basis_functions,
weights,
filtername1=filtername,
filtername2=filtername2,
exptime=exptime,
ideal_pair_time=pair_time,
survey_name=survey_name,
ignore_obs=ignore_obs,
nexp=nexp,
detailers=detailer_list,
**BlobSurvey_params,
)
)
return surveys
def ddf_surveys(
detailers=None,
season_unobs_frac=0.2,
euclid_detailers=None,
nside=None,
expt=29.2,
nexp=2,
):
"""Generate surveys for DDF observations
Parameters
----------
detailers : `list` of `rubin_scheduler.scheduler.Detailer`
Detailers for DDFs. Default None.
season_unobs_frac : `float`
Fraction of the season to not attempt DDF observations.
Default 0.2.
euclid_detailers : `list` of `rubin_scheduler.scheduler.Detailer`
Detailers to use for Euclid DDF observations.
Default None.
expt : `float`
Exposure time for DDF visits. Default 29.2.
"""
nsnaps = [1, 2, 2, 2, 2, 2]
if nexp == 1:
nsnaps = [1, 1, 1, 1, 1, 1]
obs_array = generate_ddf_scheduled_obs(season_unobs_frac=season_unobs_frac, expt=expt, nsnaps=nsnaps)
euclid_obs = np.where(
(obs_array["scheduler_note"] == "DD:EDFS_b") | (obs_array["scheduler_note"] == "DD:EDFS_a")
)[0]
all_other = np.where(
(obs_array["scheduler_note"] != "DD:EDFS_b") & (obs_array["scheduler_note"] != "DD:EDFS_a")
)[0]
survey1 = ScriptedSurvey([bf.AvoidDirectWind(nside=nside)], nside=nside, detailers=detailers)
survey1.set_script(obs_array[all_other])
survey2 = ScriptedSurvey([bf.AvoidDirectWind(nside=nside)], nside=nside, detailers=euclid_detailers)
survey2.set_script(obs_array[euclid_obs])
return [survey1, survey2]
def ecliptic_target(nside=DEFAULT_NSIDE, dist_to_eclip=40.0, dec_max=30.0, mask=None):
"""Generate a target_map for the area around the ecliptic
Parameters
----------
nside : `int`
The HEALpix nside to use
dist_to_eclip : `float`
The distance to the ecliptic to constrain to (degrees).
Default 40.
dec_max : `float`
The max declination to alow (degrees).
Default 30.
mask : `np.array`
Any additional mask to apply, should be a
HEALpix mask with matching nside. Default None.
"""
ra, dec = _hpid2_ra_dec(nside, np.arange(hp.nside2npix(nside)))
result = np.zeros(ra.size)
coord = SkyCoord(ra=ra * u.rad, dec=dec * u.rad)
eclip_lat = coord.barycentrictrueecliptic.lat.radian
good = np.where((np.abs(eclip_lat) < np.radians(dist_to_eclip)) & (dec < np.radians(dec_max)))
result[good] += 1
if mask is not None:
result *= mask
return result
[docs]
def generate_twilight_near_sun(
nside,
night_pattern=None,
nexp=1,
exptime=15,
ideal_pair_time=5.0,
max_airmass=2.0,
camera_rot_limits=[-80.0, 80.0],
time_needed=10,
footprint_mask=None,
footprint_weight=0.1,
slewtime_weight=3.0,
stayfilter_weight=3.0,
min_area=None,
filters="riz",
n_repeat=4,
sun_alt_limit=-14.8,
slew_estimate=4.5,
moon_distance=30.0,
shadow_minutes=0,
min_alt=20.0,
max_alt=76.0,
max_elong=60.0,
ignore_obs=["DD", "pair", "long", "blob", "greedy"],
filter_dist_weight=0.3,
time_to_12deg=25.0,
):
"""Generate a survey for observing NEO objects in twilight
Parameters
----------
night_pattern : `list` of `bool`
A list of bools that set when the survey will be
active. e.g., [True, False] for every-other night,
[True, False, False] for every third night.
Default None.
nexp : `int`
Number of snaps in a visit. Default 1.
exptime : `float`
Exposure time of visits. Default 15.
ideal_pair_time : `float`
Ideal time between repeat visits (minutes).
Default 5
max_airmass : `float`
Maximum airmass to attempt (unitless). Default 2.
camera_rot_limits : `list` of `float`
The camera rotation limits to use (degrees).
Default [-80, 80].
time_needed : `float`
How much time should be available
(e.g., before twilight ends) (minutes).
Default 10
footprint_mask : `np.array`
Mask to apply to the constructed ecliptic target mask (None).
Default None
footprint_weight : `float`
Weight for footprint basis function. Default 0.1 (uniteless).
slewtime_weight : `float`
Weight for slewtime basis function. Default 3 (unitless)
stayfilter_weight : `float`
Weight for staying in the same filter basis function.
Default 3 (unitless)
min_area : `float`
The area that needs to be available before the survey will return
observations (sq degrees?). Default None.
filters : `str`
The filters to use, default 'riz'
n_repeat : `int`
The number of times a blob should be repeated, default 4.
sun_alt_limit : `float`
Do not start unless sun is higher than this limit (degrees).
Default -14.8.
slew_estimate : `float`
An estimate of how long it takes to slew between
neighboring fields (seconds). Default 4.5
time_to_sunrise : `float`
Do not execute if time to sunrise is greater than (minutes).
Default 25.
"""
survey_name = "twilight_near_sun"
footprint = ecliptic_target(nside=nside, mask=footprint_mask)
constant_fp = ConstantFootprint(nside=nside)
for filtername in filters:
constant_fp.set_footprint(filtername, footprint)
surveys = []
for filtername in filters:
detailer_list = []
detailer_list.append(
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
)
detailer_list.append(detailers.CloseAltDetailer())
# Should put in a detailer so things start at lowest altitude
detailer_list.append(detailers.TwilightTripleDetailer(slew_estimate=slew_estimate, n_repeat=n_repeat))
detailer_list.append(detailers.RandomFilterDetailer(filters=filters))
bfs = []
bfs.append(
(
bf.FootprintBasisFunction(
filtername=filtername,
footprint=constant_fp,
out_of_bounds_val=np.nan,
nside=nside,
),
footprint_weight,
)
)
bfs.append(
(
bf.SlewtimeBasisFunction(filtername=filtername, nside=nside),
slewtime_weight,
)
)
bfs.append((bf.StrictFilterBasisFunction(filtername=filtername), stayfilter_weight))
bfs.append((bf.FilterDistBasisFunction(filtername=filtername), filter_dist_weight))
# Need a toward the sun, reward high airmass, with an
# airmass cutoff basis function.
bfs.append(
(
bf.NearSunHighAirmassBasisFunction(nside=nside, max_airmass=max_airmass),
0,
)
)
bfs.append(
(
bf.AltAzShadowMaskBasisFunction(
nside=nside,
shadow_minutes=shadow_minutes,
max_alt=max_alt,
min_alt=min_alt,
pad=3.0,
),
0,
)
)
bfs.append((bf.MoonAvoidanceBasisFunction(nside=nside, moon_distance=moon_distance), 0))
bfs.append((bf.FilterLoadedBasisFunction(filternames=filtername), 0))
bfs.append((bf.PlanetMaskBasisFunction(nside=nside), 0))
bfs.append(
(
bf.SolarElongationMaskBasisFunction(min_elong=0.0, max_elong=max_elong, nside=nside),
0,
)
)
bfs.append((bf.NightModuloBasisFunction(pattern=night_pattern), 0))
# Do not attempt unless the sun is getting high
bfs.append(
(
(
bf.CloseToTwilightBasisFunction(
max_sun_alt_limit=sun_alt_limit, max_time_to_12deg=time_to_12deg
)
),
0,
)
)
# unpack the basis functions and weights
weights = [val[1] for val in bfs]
basis_functions = [val[0] for val in bfs]
# Set huge ideal pair time and use the detailer to cut down
# the list of observations to fit twilight?
surveys.append(
BlobSurvey(
basis_functions,
weights,
filtername1=filtername,
filtername2=None,
ideal_pair_time=ideal_pair_time,
nside=nside,
exptime=exptime,
survey_name=survey_name,
ignore_obs=ignore_obs,
dither=True,
nexp=nexp,
detailers=detailer_list,
twilight_scale=False,
min_area=min_area,
)
)
return surveys
[docs]
def set_run_info(dbroot=None, file_end="v3.4_", out_dir="."):
"""Gather versions of software used to record"""
extra_info = {}
exec_command = ""
for arg in sys.argv:
exec_command += " " + arg
extra_info["exec command"] = exec_command
try:
extra_info["git hash"] = subprocess.check_output(["git", "rev-parse", "HEAD"])
except subprocess.CalledProcessError:
extra_info["git hash"] = "Not in git repo"
extra_info["file executed"] = os.path.realpath(__file__)
try:
rs_path = rubin_scheduler.__path__[0]
hash_file = os.path.join(rs_path, "../", ".git/refs/heads/main")
extra_info["rubin_scheduler git hash"] = subprocess.check_output(["cat", hash_file])
except subprocess.CalledProcessError:
pass
# Use the filename of the script to name the output database
if dbroot is None:
fileroot = os.path.basename(sys.argv[0]).replace(".py", "") + "_"
else:
fileroot = dbroot + "_"
fileroot = os.path.join(out_dir, fileroot + file_end)
return fileroot, extra_info
[docs]
def run_sched(
scheduler,
survey_length=365.25,
nside=DEFAULT_NSIDE,
filename=None,
verbose=False,
extra_info=None,
illum_limit=40.0,
mjd_start=60796.0,
event_table=None,
sim_to_o=None,
):
"""Run survey"""
n_visit_limit = None
fs = SimpleFilterSched(illum_limit=illum_limit)
observatory = ModelObservatory(nside=nside, mjd_start=mjd_start, sim_to_o=sim_to_o)
observatory, scheduler, observations = sim_runner(
observatory,
scheduler,
sim_duration=survey_length,
filename=filename,
delete_past=True,
n_visit_limit=n_visit_limit,
verbose=verbose,
extra_info=extra_info,
filter_scheduler=fs,
event_table=event_table,
)
return observatory, scheduler, observations
def gen_scheduler(args):
survey_length = args.survey_length # Days
out_dir = args.out_dir
verbose = args.verbose
nexp = args.nexp
dbroot = args.dbroot
nside = args.nside
mjd_plus = args.mjd_plus
split_long = args.split_long
too = ~args.no_too
# Parameters that were previously command-line
# arguments.
max_dither = 0.2 # Degrees. For DDFs
ddf_season_frac = 0.2 # Amount of season to use for DDFs
illum_limit = 40.0 # Percent. Lunar illumination used for filter loading
u_exptime = 38.0 # Deconds
nslice = 2 # N slices for rolling
rolling_scale = 0.9 # Strength of rolling
rolling_uniform = True # Should we use the uniform rolling flag
nights_off = 3 # For long gaps
ei_night_pattern = 4 # select doing earth interior observation every 4 nights
ei_filters = "riz" # Filters to use for earth interior observations.
ei_repeat = 4 # Number of times to repeat earth interior observations
ei_am = 2.5 # Earth interior airmass limit
ei_elong_req = 45.0 # Solar elongation required for inner solar system
ei_area_req = 0.0 # Sky area required before attempting inner solar system
per_night = True # Dither DDF per night
camera_ddf_rot_limit = 75.0 # degrees
# Be sure to also update and regenerate DDF grid save file
# if changing mjd_start
mjd_start = SURVEY_START_MJD + mjd_plus
fileroot, extra_info = set_run_info(dbroot=dbroot, file_end="v4.0_", out_dir=out_dir)
pattern_dict = {
1: [True],
2: [True, False],
3: [True, False, False],
4: [True, False, False, False],
# 4 on, 4 off
5: [True, True, True, True, False, False, False, False],
# 3 on 4 off
6: [True, True, True, False, False, False, False],
7: [True, True, False, False, False, False],
}
ei_night_pattern = pattern_dict[ei_night_pattern]
reverse_ei_night_pattern = [not val for val in ei_night_pattern]
sky = CurrentAreaMap(nside=nside)
footprints_hp_array, labels = sky.return_maps()
wfd_indx = np.where((labels == "lowdust") | (labels == "virgo"))[0]
wfd_footprint = footprints_hp_array["r"] * 0
wfd_footprint[wfd_indx] = 1
footprints_hp = {}
for key in footprints_hp_array.dtype.names:
footprints_hp[key] = footprints_hp_array[key]
footprint_mask = footprints_hp["r"] * 0
footprint_mask[np.where(footprints_hp["r"] > 0)] = 1
repeat_night_weight = None
# Use the Almanac to find the position of the sun at the start of survey
almanac = Almanac(mjd_start=mjd_start)
sun_moon_info = almanac.get_sun_moon_positions(mjd_start)
sun_ra_start = sun_moon_info["sun_RA"].copy()
footprints = make_rolling_footprints(
fp_hp=footprints_hp,
mjd_start=mjd_start,
sun_ra_start=sun_ra_start,
nslice=nslice,
scale=rolling_scale,
nside=nside,
wfd_indx=wfd_indx,
order_roll=1,
n_cycles=3,
uniform=rolling_uniform,
)
gaps_night_pattern = [True] + [False] * nights_off
long_gaps = gen_long_gaps_survey(
nside=nside,
footprints=footprints,
night_pattern=gaps_night_pattern,
u_exptime=u_exptime,
nexp=nexp,
)
# Set up the DDF surveys to dither
u_detailer = detailers.FilterNexp(filtername="u", nexp=1, exptime=u_exptime)
dither_detailer = detailers.DitherDetailer(per_night=per_night, max_dither=max_dither)
details = [
detailers.CameraRotDetailer(min_rot=-camera_ddf_rot_limit, max_rot=camera_ddf_rot_limit),
dither_detailer,
u_detailer,
detailers.Rottep2RotspDesiredDetailer(),
]
euclid_detailers = [
detailers.CameraRotDetailer(min_rot=-camera_ddf_rot_limit, max_rot=camera_ddf_rot_limit),
detailers.EuclidDitherDetailer(),
u_detailer,
detailers.Rottep2RotspDesiredDetailer(),
]
ddfs = ddf_surveys(
detailers=details,
season_unobs_frac=ddf_season_frac,
euclid_detailers=euclid_detailers,
nside=nside,
nexp=nexp,
)
greedy = gen_greedy_surveys(nside, nexp=nexp, footprints=footprints)
neo = generate_twilight_near_sun(
nside,
night_pattern=ei_night_pattern,
filters=ei_filters,
n_repeat=ei_repeat,
footprint_mask=footprint_mask,
max_airmass=ei_am,
max_elong=ei_elong_req,
min_area=ei_area_req,
)
blobs = generate_blobs(
nside,
nexp=nexp,
footprints=footprints,
mjd_start=mjd_start,
u_exptime=u_exptime,
)
twi_blobs = generate_twi_blobs(
nside,
nexp=nexp,
footprints=footprints,
wfd_footprint=wfd_footprint,
repeat_night_weight=repeat_night_weight,
night_pattern=reverse_ei_night_pattern,
)
roman_surveys = [
gen_roman_on_season(nexp=nexp, exptime=29.2),
gen_roman_off_season(nexp=nexp, exptime=29.2),
]
if too:
too_scale = 1.0
sim_ToOs, event_table = gen_all_events(scale=too_scale, nside=nside)
camera_rot_limits = [-80.0, 80.0]
detailer_list = []
detailer_list.append(
detailers.CameraRotDetailer(min_rot=np.min(camera_rot_limits), max_rot=np.max(camera_rot_limits))
)
# Let's make a footprint to follow up ToO events
too_footprint = footprints_hp["r"] * 0 + np.nan
too_footprint[np.where(footprints_hp["r"] > 0)[0]] = 1.0
detailer_list.append(detailers.Rottep2RotspDesiredDetailer())
toos = gen_too_surveys(
nside=nside,
detailer_list=detailer_list,
too_footprint=too_footprint,
split_long=split_long,
n_snaps=nexp,
)
surveys = [toos, roman_surveys, ddfs, long_gaps, blobs, twi_blobs, neo, greedy]
else:
surveys = [roman_surveys, ddfs, long_gaps, blobs, twi_blobs, neo, greedy]
sim_ToOs = None
event_table = None
fileroot = fileroot.replace("baseline", "no_too")
scheduler = CoreScheduler(surveys, nside=nside)
if args.setup_only:
return scheduler
else:
years = np.round(survey_length / 365.25)
observatory, scheduler, observations = run_sched(
scheduler,
survey_length=survey_length,
verbose=verbose,
filename=os.path.join(fileroot + "%iyrs.db" % years),
extra_info=extra_info,
nside=nside,
illum_limit=illum_limit,
mjd_start=mjd_start,
event_table=event_table,
sim_to_o=sim_ToOs,
)
return observatory, scheduler, observations
def sched_argparser():
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", dest="verbose", action="store_true", help="Print more output")
parser.set_defaults(verbose=False)
parser.add_argument("--survey_length", type=float, default=365.25 * 10, help="Survey length in days")
parser.add_argument("--out_dir", type=str, default="", help="Output directory")
parser.add_argument("--nexp", type=int, default=2, help="Number of exposures per visit")
parser.add_argument("--dbroot", type=str, help="Database root")
parser.add_argument(
"--setup_only",
dest="setup_only",
default=False,
action="store_true",
help="Only construct scheduler, do not simulate",
)
parser.add_argument(
"--nside",
type=int,
default=DEFAULT_NSIDE,
help="Nside should be set to default (32) except for tests.",
)
parser.add_argument(
"--mjd_plus",
type=float,
default=0,
help="number of days to add to the mjd start",
)
parser.add_argument(
"--split_long",
dest="split_long",
action="store_true",
help="Split long ToO exposures into standard visit lengths",
)
parser.set_defaults(split_long=False)
parser.add_argument("--no_too", dest="no_too", action="store_true")
parser.set_defaults(no_too=False)
return parser
if __name__ == "__main__":
parser = sched_argparser()
args = parser.parse_args()
gen_scheduler(args)