Feature Based Scheduler run with SOCS and custom scheduler configuration

This example notebook show how to do a slightly more complex 1 day FBS run using SOCS. In this example we use a custom FBS configuration done directly on the notebook.

Before running the notebook make sure you run manage_db --save-dir $HOME/run_local/output/ on the command line to setup the SOCS database.

[1]:
import logging
import healpy as hp
import numpy as np

import lsst.sims.featureScheduler as fs

from lsst.sims.featureScheduler.driver import FeatureSchedulerDriver as Driver

from lsst.sims.ocs.database import SocsDatabase
from lsst.sims.ocs.kernel import Simulator
from lsst.sims.ocs.setup import create_parser
from lsst.sims.ocs.setup import apply_file_config, read_file_config

from lsst.ts.scheduler.kernel import SurveyTopology
[2]:
logging.getLogger().setLevel(logging.INFO)
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M')

This next cell loads default command line arguments. These are needed mainly to setup the simulation database.

[3]:
parser = create_parser()
args = parser.parse_known_args()[0]
prog_conf = read_file_config()
if prog_conf is not None:
    apply_file_config(prog_conf, args)
print(args.sqlite_save_dir, args.session_id_start, args.sqlite_session_save_dir)
/home/opsim/run_local/output/ None None

Setup socs database to store simulations results, if needed.

[4]:
db = SocsDatabase(sqlite_save_path=args.sqlite_save_dir,
                  session_id_start=args.session_id_start,
                  sqlite_session_save_path=args.sqlite_session_save_dir)
[5]:
session_id = db.new_session("FBS test on notebook")

We now define a driver for the simulation. In this case, we already imported the FBS driver as Driver so we simply call it.

[6]:
driver = Driver()

By default the duration of a simulation is 10 years. Here we will run a single day.

[7]:
args.frac_duration = 0.003

We now set the SOCS simulator

[8]:
sim = Simulator(args, db, driver=driver)

Up to this point, the scheduler is still not configured. Let’s make a custom configuration.

[9]:
survey_topology = SurveyTopology()
survey_topology.num_general_props = 4
survey_topology.general_propos = ["NorthEclipticSpur", "SouthCelestialPole", "WideFastDeep", "GalacticPlane"]
survey_topology.num_seq_props = 1
survey_topology.sequence_propos = ["DeepDrillingCosmology1"]

target_maps = {}
nside = fs.set_default_nside(nside=32)  # Required

target_maps['u'] = fs.generate_goal_map(NES_fraction=0.,
                                        WFD_fraction=0.31, SCP_fraction=0.08,
                                        GP_fraction=0.004,
                                        WFD_upper_edge_fraction=0.0,
                                        nside=nside,
                                        generate_id_map=True)
target_maps['g'] = fs.generate_goal_map(NES_fraction=0.2,
                                        WFD_fraction=0.44, SCP_fraction=0.08,
                                        GP_fraction=0.004,
                                        WFD_upper_edge_fraction=0.0,
                                        nside=nside,
                                        generate_id_map=True)
target_maps['r'] = fs.generate_goal_map(NES_fraction=0.46,
                                        WFD_fraction=1.0, SCP_fraction=0.08,
                                        WFD_upper_edge_fraction=0.0,
                                        GP_fraction=0.004,
                                        nside=nside,
                                        generate_id_map=True)
target_maps['i'] = fs.generate_goal_map(NES_fraction=0.46,
                                        WFD_fraction=1.0, SCP_fraction=0.08,
                                        GP_fraction=0.004,
                                        WFD_upper_edge_fraction=0.0,
                                        nside=nside,
                                        generate_id_map=True)
target_maps['z'] = fs.generate_goal_map(NES_fraction=0.4,
                                        WFD_fraction=0.9, SCP_fraction=0.08,
                                        GP_fraction=0.004,
                                        WFD_upper_edge_fraction=0.0,
                                        nside=nside,
                                        generate_id_map=True)
target_maps['y'] = fs.generate_goal_map(NES_fraction=0.,
                                        WFD_fraction=0.9, SCP_fraction=0.08,
                                        GP_fraction=0.004,
                                        WFD_upper_edge_fraction=0.0,
                                        nside=nside,
                                        generate_id_map=True)

cloud_map = fs.utils.generate_cloud_map(target_maps, filtername='r',
                                        wfd_cloud_max=0.7,
                                        scp_cloud_max=0.7,
                                        gp_cloud_max=0.7,
                                        nes_cloud_max=0.7)

# x1 = 30.
# x0 = 2.
# B = x1 / (x1 - x0)
# A = -B / x1
# width = np.arange(x0, x1, 0.5)
# z_pad = width + 8.
# weight = (A * width + B)
# height = np.zeros_like(width) + 80.

width = (10.,)
z_pad = (18.,)
weight = (1.,)
height = (80.,)

filters = ['u', 'g', 'r', 'i', 'z', 'y']
surveys = []

sb_limit_map = fs.utils.generate_sb_map(target_maps, filters)

filter_prop = {'u': 0.069,
               'g': 0.097,
               'r': 0.222,
               'i': 0.222,
               'z': 0.194,
               'y': 0.194}

for filtername in filters:
    bfs = list()
    # bfs.append(fs.M5_diff_basis_function(filtername=filtername, nside=nside))
    bfs.append(fs.HourAngle_bonus_basis_function(max_hourangle=3.))
    bfs.append(fs.Skybrightness_limit_basis_function(nside=nside,
                                                     filtername=filtername,
                                                     min=sb_limit_map[filtername]['min'],
                                                     max=sb_limit_map[filtername]['max']))
    bfs.append(fs.Target_map_basis_function(filtername=filtername,
                                            target_map=target_maps[filtername][0],
                                            out_of_bounds_val=hp.UNSEEN, nside=nside))
    bfs.append(fs.MeridianStripeBasisFunction(nside=nside,width=width,
                                              weight=weight,
                                              height=height,
                                              zenith_pad=z_pad))
    # bfs.append(fs.HADecAltAzPatchBasisFunction(nside=nside,
    #                                            patches=patches[::-1]))
    bfs.append(fs.Aggressive_Slewtime_basis_function(filtername=filtername, nside=nside, order=6., hard_max=20.))
    bfs.append(fs.Goal_Strict_filter_basis_function(filtername=filtername,
                                               time_lag_min=90.,
                                               time_lag_max=150.,
                                               time_lag_boost=180.,
                                               boost_gain=1.0,
                                               unseen_before_lag=True,
                                               proportion=filter_prop[filtername],
                                               aways_available=filtername in 'zy'))
    bfs.append(fs.Avoid_Fast_Revists(filtername=None, gap_min=240., nside=nside))
    bfs.append(fs.Bulk_cloud_basis_function(max_cloud_map=cloud_map, nside=nside))
    bfs.append(fs.Moon_avoidance_basis_function(nside=nside, moon_distance=40.))
    # bfs.append(fs.CableWrap_unwrap_basis_function(nside=nside, activate_tol=70., unwrap_until=315,
    #                                               max_duration=90.))
    # bfs.append(fs.NorthSouth_scan_basis_function(length=70.))

    weights = np.array([2., 0.1, .1, 1., 3., 1.5, 1.0, 1.0, 1.0])
    surveys.append(fs.Greedy_survey_fields(bfs, weights, block_size=1,
                                           filtername=filtername, dither=True,
                                           nside=nside,
                                           tag_fields=True,
                                           tag_map=target_maps[filtername][1],
                                           tag_names=target_maps[filtername][2],
                                           ignore_obs='DD'))

# Set up pairs
pairs_bfs = []

pair_map = np.zeros(len(target_maps['z'][0]))
pair_map.fill(hp.UNSEEN)
wfd = np.where(target_maps['z'][1] == 3)
nes = np.where(target_maps['z'][1] == 1)
pair_map[wfd] = 1.
pair_map[nes] = 1.

pairs_bfs.append(fs.Target_map_basis_function(filtername='',
                                              target_map=pair_map,
                                              out_of_bounds_val=hp.UNSEEN, nside=nside))
pairs_bfs.append(fs.MeridianStripeBasisFunction(nside=nside, zenith_pad=(45.,), width=(35.,)))
pairs_bfs.append(fs.Moon_avoidance_basis_function(nside=nside, moon_distance=30.))

# surveys.append(fs.Pairs_survey_scripted(pairs_bfs, [1., 1., 1.], ignore_obs='DD', min_alt=20.))
surveys.append(fs.Pairs_different_filters_scripted(pairs_bfs, [1., 1., 1.], ignore_obs='DD', min_alt=20.,
                                                   filter_goals=filter_prop))
# surveys.append(fs.Pairs_survey_scripted([], [], ignore_obs='DD'))

# Set up the DD
# ELAIS S1
surveys.append(fs.Deep_drilling_survey(9.45, -44., sequence='rgizy',
                                       nvis=[20, 10, 20, 26, 20],
                                       survey_name='DD:ELAISS1', reward_value=100, moon_up=None,
                                       fraction_limit=0.148, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside,
                                       avoid_same_day=True,
                                       filter_goals=filter_prop))
surveys.append(fs.Deep_drilling_survey(9.45, -44., sequence='u',
                                       nvis=[7],
                                       survey_name='DD:u,ELAISS1', reward_value=100, moon_up=False,
                                       fraction_limit=0.0012, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside))

# XMM-LSS
surveys.append(fs.Deep_drilling_survey(35.708333, -4 - 45 / 60., sequence='rgizy',
                                       nvis=[20, 10, 20, 26, 20],
                                       survey_name='DD:XMM-LSS', reward_value=100, moon_up=None,
                                       fraction_limit=0.148, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside,
                                       avoid_same_day=True,
                                       filter_goals=filter_prop))
surveys.append(fs.Deep_drilling_survey(35.708333, -4 - 45 / 60., sequence='u',
                                       nvis=[7],
                                       survey_name='DD:u,XMM-LSS', reward_value=100, moon_up=False,
                                       fraction_limit=0.0012, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside))

# Extended Chandra Deep Field South
# XXX--Note, this one can pass near zenith. Should go back and add better planning on this.
surveys.append(fs.Deep_drilling_survey(53.125, -28. - 6 / 60., sequence='rgizy',
                                       nvis=[20, 10, 20, 26, 20],
                                       survey_name='DD:ECDFS', reward_value=100, moon_up=None,
                                       fraction_limit=0.148, ha_limits=[[0.5, 1.0], [23., 22.5]],
                                       nside=nside,
                                       avoid_same_day=True,
                                       filter_goals=filter_prop))
surveys.append(fs.Deep_drilling_survey(53.125, -28. - 6 / 60., sequence='u',
                                       nvis=[7],
                                       survey_name='DD:u,ECDFS', reward_value=100, moon_up=False,
                                       fraction_limit=0.0012, ha_limits=[[0.5, 1.0], [23., 22.5]],
                                       nside=nside))
# COSMOS
surveys.append(fs.Deep_drilling_survey(150.1, 2. + 10. / 60. + 55 / 3600., sequence='rgizy',
                                       nvis=[20, 10, 20, 26, 20],
                                       survey_name='DD:COSMOS', reward_value=100, moon_up=None,
                                       fraction_limit=0.148, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside,
                                       avoid_same_day=True,
                                       filter_goals=filter_prop))
surveys.append(fs.Deep_drilling_survey(150.1, 2. + 10. / 60. + 55 / 3600., sequence='u',
                                       nvis=[7], ha_limits=([0., .5], [23.5, 24.]),
                                       survey_name='DD:u,COSMOS', reward_value=100, moon_up=False,
                                       fraction_limit=0.0012,
                                       nside=nside))

# Extra DD Field, just to get to 5. Still not closed on this one
surveys.append(fs.Deep_drilling_survey(349.386443, -63.321004, sequence='rgizy',
                                       nvis=[20, 10, 20, 26, 20],
                                       survey_name='DD:290', reward_value=100, moon_up=None,
                                       fraction_limit=0.148, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside,
                                       avoid_same_day=True,
                                       filter_goals=filter_prop))
surveys.append(fs.Deep_drilling_survey(349.386443, -63.321004, sequence='u',
                                       nvis=[7],
                                       survey_name='DD:u,290', reward_value=100, moon_up=False,
                                       fraction_limit=0.0012, ha_limits=([0., 0.5], [23.5, 24.]),
                                       nside=nside,
                                       filter_goals=filter_prop))

scheduler = fs.Core_scheduler(surveys, nside=nside)  # Required

Now we load the configuration to driver. We basically need to two two steps, load scheduler, nside and survey_topology.

[10]:
sim.driver.scheduler = scheduler
sim.driver.sky_nside = nside

sim.conf_comm.num_proposals = survey_topology.num_props
sim.conf_comm.survey_topology['general'] = survey_topology.general_propos
sim.conf_comm.survey_topology['sequence'] = survey_topology.sequence_propos

We now initialize the simulator.

[11]:
sim.initialize()
09-11 19:44 kernel.Simulator INFO     Initializing simulation
09-11 19:44 kernel.Simulator INFO     Simulation Session Id = 2002
09-11 19:44 configuration.ConfigurationCommunicator INFO     Initializing configuration communication
09-11 19:44 kernel.Simulator INFO     Finishing simulation initialization

And we are ready to run the simulation.

[12]:
sim.run()
09-11 19:44 kernel.Simulator INFO     Starting simulation
09-11 19:44 kernel.Simulator INFO     run: rx scheduler config survey_duration=3650.0
09-11 19:44 kernel.Simulator INFO     run: rx driver config={'ranking': {'coadd_values': 1, 'time_balancing': 1, 'timecost_time_max': 150.0, 'timecost_time_ref': 5.0, 'timecost_cost_ref': 0.3, 'timecost_weight': 1.0, 'filtercost_weight': 1.0, 'propboost_weight': 1.0, 'lookahead_window_size': 0, 'lookahead_bonus_weight': 0.0}, 'constraints': {'night_boundary': -12.0, 'ignore_sky_brightness': 0, 'ignore_airmass': 0, 'ignore_clouds': 0, 'ignore_seeing': 0}, 'darktime': {'new_moon_phase_threshold': 20.0}, 'startup': {'type': 'HOT', 'database': ''}}
09-11 19:44 kernel.Simulator INFO     run: rx site config={'obs_site': {'name': 'Cerro Pachon', 'latitude': -30.2444, 'longitude': -70.7494, 'height': 2650.0}}
09-11 19:44 kernel.Simulator INFO     run: rx telescope config={'telescope': {'altitude_minpos': 20.0, 'altitude_maxpos': 86.5, 'azimuth_minpos': -270.0, 'azimuth_maxpos': 270.0, 'altitude_maxspeed': 3.5, 'altitude_accel': 3.5, 'altitude_decel': 3.5, 'azimuth_maxspeed': 7.0, 'azimuth_accel': 7.0, 'azimuth_decel': 7.0, 'settle_time': 3.0}}
09-11 19:44 kernel.Simulator INFO     run: rx dome config={'dome': {'altitude_maxspeed': 1.75, 'altitude_accel': 0.875, 'altitude_decel': 0.875, 'altitude_freerange': 0.0, 'azimuth_maxspeed': 1.5, 'azimuth_accel': 0.75, 'azimuth_decel': 0.75, 'azimuth_freerange': 4.0, 'settle_time': 0.0}}
09-11 19:44 kernel.Simulator INFO     run: rx rotator config={'rotator': {'minpos': -90.0, 'maxpos': 90.0, 'maxspeed': 3.5, 'accel': 1.0, 'decel': 1.0, 'filter_change_pos': 0.0, 'follow_sky': 0, 'resume_angle': 0}}
09-11 19:44 kernel.Simulator INFO     run: rx camera config={'camera': {'readout_time': 2.0, 'shutter_time': 1.0, 'filter_change_time': 120.0, 'filter_max_changes_burst_num': 1, 'filter_max_changes_burst_time': 0.0, 'filter_max_changes_avg_num': 3000, 'filter_max_changes_avg_time': 31557600.0, 'filter_removable': ['y', 'z'], 'filter_mounted': ['g', 'r', 'i', 'z', 'y'], 'filter_unmounted': ['u']}}
09-11 19:44 kernel.Simulator INFO     run: rx slew config={'slew': {'prereq_domalt': [], 'prereq_domaz': [], 'prereq_domazsettle': ['domaz'], 'prereq_telalt': [], 'prereq_telaz': [], 'prereq_telopticsopenloop': ['telalt', 'telaz'], 'prereq_telopticsclosedloop': ['domalt', 'domazsettle', 'telsettle', 'readout', 'telopticsopenloop', 'filter', 'telrot'], 'prereq_telsettle': ['telalt', 'telaz'], 'prereq_telrot': [], 'prereq_filter': [], 'prereq_exposures': ['telopticsclosedloop'], 'prereq_readout': []}}
09-11 19:44 kernel.Simulator INFO     run: rx optics config={'optics_loop_corr': {'tel_optics_ol_slope': 0.2857142857142857, 'tel_optics_cl_alt_limit': [0.0, 9.0, 90.0], 'tel_optics_cl_delay': [0.0, 36.0]}}
09-11 19:44 kernel.Simulator INFO     run: rx park config={'park': {'telescope_altitude': 86.5, 'telescope_azimuth': 0.0, 'telescope_rotator': 0.0, 'dome_altitude': 90.0, 'dome_azimuth': 0.0, 'filter_position': 'z'}}
09-11 19:44 featureSchedulerDriver INFO     Scheduler already configured.
09-11 19:44 featureSchedulerDriver INFO     Start up type is HOT, no state will be read from the EFD.
09-11 19:44 kernel.Simulator INFO     Night 1
09-11 19:44 featureSchedulerDriver INFO     start_survey t=1664580953.883245
09-11 19:44 featureSchedulerDriver INFO     start_night t=1664580953.883245, night=1 timeprogress=0.00%
09-11 19:44 featureSchedulerDriver INFO     start_night t=1664580953.883245, night=1 timeprogress=0.00%
/home/opsim/repos/sims_skybrightness_pre/python/lsst/sims/skybrightness_pre/SkyModelPre.py:363: UserWarning: Requested MJD between sunrise and sunset, returning closest maps
  warnings.warn('Requested MJD between sunrise and sunset, returning closest maps')
/home/opsim/repos/sims_skybrightness_pre/python/lsst/sims/skybrightness_pre/SkyModelPre.py:279: UserWarning: Requested MJD between sunrise and sunset, returning closest maps
  warnings.warn('Requested MJD between sunrise and sunset, returning closest maps')
/home/opsim/repos/sims_seeingModel/python/lsst/sims/seeingModel/seeingModel.py:133: RuntimeWarning: invalid value encountered in power
  airmass_correction = np.power(airmass, 0.6)
/home/opsim/repos/sims_skybrightness_pre/python/lsst/sims/skybrightness_pre/SkyModelPre.py:49: RuntimeWarning: invalid value encountered in true_divide
  wterm = (x - xp[left])/baseline
09-11 19:45 featureSchedulerDriver INFO     end_night t=1664616564.504870, night=1 timeprogress=0.01%
09-11 19:45 featureSchedulerDriver INFO     end_night next moonphase=40.78%
09-11 19:45 featureSchedulerDriver INFO     end_night bright time waxing

We now have access to all the scheduler data structure to play with. In the cell bellow, we plot the TargetMapBasis function for the r filter.

[13]:
hp.mollview(sim.driver.scheduler.survey_lists[0][2].basis_functions[2]())
../_images/notebooks_complex_fbs_with_socs_24_0.png
[ ]: