Skip to content

Commit

Permalink
Add new features for extension; in particular, allowing one program t…
Browse files Browse the repository at this point in the history
…o depend on another program.
  • Loading branch information
schlafly committed Sep 30, 2024
1 parent d97b77b commit 3c8e52c
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 22 deletions.
8 changes: 3 additions & 5 deletions py/desisurvey/ephem.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@
import desisurvey.tiles


# Date range 2019-2025 for tabulated ephemerides.
# This range is chosen large enough to cover commissioning,
# survey validation and the 5-year main survey, so should
# not normally need to be changed, except for testing.
# Date range 2019-2029 for tabulated ephemerides.
# This range is intended to cover out through the DESI extension.
START_DATE = datetime.date(2019, 1, 1)
STOP_DATE = datetime.date(2027, 12, 31)
STOP_DATE = datetime.date(2029, 12, 31)

_ephem = None

Expand Down
24 changes: 24 additions & 0 deletions py/desisurvey/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import astropy.table
import astropy.io.fits
from astropy.coordinates import SkyCoord
from astropy import units as u

import desiutil.log

Expand Down Expand Up @@ -512,6 +514,7 @@ def afternoon_plan(self, night):
self.add_pending_tile(tileid)
if self.tiles_lowpass:
self.prefer_low_passnum()
self.require_program_dependencies()
zeropriority = ((self.tile_status != 'unobs') &
(self.tile_available == 0))
self.tile_priority[zeropriority] = 0
Expand Down Expand Up @@ -559,3 +562,24 @@ def prefer_low_passnum(self):
# unobserved, overlapping tiles with lower pass numbers.
mfree[idx] = 0
self.tile_available[~mfree] = 0

def require_program_dependencies(self):
depends_on = self.tiles.program_dependencies()
config = desisurvey.config.Configuration()
tile_diameter = 2 * config.tile_radius()

for program in depends_on:
mprog = self.tiles.program_mask[program]
mtodo = ((self.tile_status != 'done') &
(self.tiles.in_desi != 0))
otherprog = depends_on[program]
# tiles we still intend to do in the underlying program
motherprog = self.tiles.program_mask[otherprog] & mtodo
cprog = SkyCoord(self.tiles.tileRA[mprog]*u.deg,
self.tiles.tileDEC[mprog]*u.deg)
cotherprog = SkyCoord(self.tiles.tileRA[motherprog]*u.deg,
self.tiles.tileDEC[motherprog]*u.deg)
idxo, idxp, _, _ = cprog.search_around_sky(cotherprog, tile_diameter)
mblocked = np.zeros(len(mprog), dtype='bool')
mblocked[np.flatnonzero(mprog)[idxp]] = True
self.tile_available[mblocked] = 0
3 changes: 2 additions & 1 deletion py/desisurvey/plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

# Color associated with each program in the functions below.
program_color = {'DARK': 'black', 'GRAY': 'gray', 'BRIGHT': 'orange',
'BACKUP': 'green'}
'BACKUP': 'green', 'DARK1B': 'purple',
'BRIGHT1B': 'red'}


def plot_sky_passes(ra, dec, passnum, z, clip_lo=None, clip_hi=None,
Expand Down
11 changes: 7 additions & 4 deletions py/desisurvey/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,10 @@ def conditions_to_program(self, seeing, transparency, skylevel,
return 'BRIGHT'
return 'BACKUP'

def select_program(self, mjd_now, ETC, verbose=False,
def current_conditions(self, mjd_now, ETC, verbose=False,
seeing=None, transparency=None, skylevel=None,
airmass=None, speed=None):
"""Select program to observe now.
"""Return current conditions, based on ephemerides or speed.
"""
if mjd_now < self.night_changes[0]:
if verbose:
Expand Down Expand Up @@ -248,7 +248,9 @@ def select_program(self, mjd_now, ETC, verbose=False,
else:
mjd_program_end = self.night_changes[-1]
return program, mjd_program_end

# select program based on ephemerides, not conditions.
# we have not actually used this in DESI.
idx = 0
while ((idx + 1 < len(self.night_changes)) and
(mjd_now >= self.night_changes[idx + 1])):
Expand Down Expand Up @@ -348,14 +350,15 @@ def next_tile(self, mjd_now, ETC, seeing, transp, skylevel, HA_sigma=15.,
self.tile_sel = np.ones(self.tiles.ntiles, dtype=bool)
if program is None:
# Which program are we in?
program, mjd_program_end = self.select_program(
conditions, mjd_program_end = self.current_conditions(
mjd_now, ETC, verbose=verbose, seeing=seeing,
skylevel=skylevel, transparency=transp, speed=speed)
self.tile_sel &= self.tiles.allowed_in_conditions(program)
self.tile_sel &= self.tiles.allowed_in_conditions(conditions)
if verbose:
self.log.info(
'Selecting a tile observable in {} conditions.'.format(
program))
program = conditions
else:
self.tile_sel &= self.tiles.program_mask[program]
mjd_program_end = self.night_changes[-1] # end of night?
Expand Down
8 changes: 7 additions & 1 deletion py/desisurvey/scripts/surveyinit.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ def parse(options=None):
parser.add_argument(
'--start', default=None, type=str,
help='Use this start date instead of config.')
parser.add_argument(
'--stop', default=None, type=str,
help='Use this stop date instead of config.')

if options is None:
args = parser.parse_args()
Expand Down Expand Up @@ -163,7 +166,10 @@ def calculate_initial_plan(args):
start = config.first_day()
else:
start = desisurvey.utils.get_date(args.start)
stop = config.last_day()
if args.stop is None:
stop = config.last_day()
else:
stop = desisurvey.utils.get_date(args.stop)
assert start >= first and stop <= last
hdr['START'] = start.isoformat()
hdr['STOP'] = stop.isoformat()
Expand Down
13 changes: 7 additions & 6 deletions py/desisurvey/scripts/surveymovie.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __init__(self, exposures_path, start, stop, label, show_scores):
self.tileid = self.tiles.tileID
self.prognames = [p for p in self.tiles.programs]
# if DARK and BRIGHT are names, put them first
for pname in ['BRIGHT', 'DARK']:
for pname in ['BRIGHT1B', 'DARK1B', 'BRIGHT', 'DARK']:
if pname in self.prognames:
self.prognames.remove(pname)
self.prognames = [pname] + self.prognames
Expand Down Expand Up @@ -200,7 +200,7 @@ def init_figure(self, nightly, width=1920, height=1080, dpi=32):
self.figure = plt.figure(
frameon=False,figsize=(width / self.dpi, height / self.dpi),
dpi=self.dpi)
grid = matplotlib.gridspec.GridSpec(2, 2)
grid = matplotlib.gridspec.GridSpec(3, 2)
grid.update(left=0, right=1, bottom=0, top=0.97, hspace=0, wspace=0)
axes = []
self.labels = []
Expand All @@ -222,15 +222,15 @@ def init_figure(self, nightly, width=1920, height=1080, dpi=32):
self.nowcolor = np.array([0., 0.7, 0., 1.])
pcolors = desisurvey.plots.program_color
progidx = 0
for row in range(2):
for row in range(3):
for col in range(2):
# Create the axes for this program.
ax = plt.subplot(grid[row, col], facecolor=bgcolor)
ax.set_xticks([])
ax.set_yticks([])
axes.append(ax)
# Bottom-right corner is reserved for integrated progress plots.
if row == 1 and col == 1:
if row == 2 and col == 1:
ax.set_xlim(0, self.survey_weeks)
ax.set_ylim(0, 1)
ax.plot([0, self.survey_weeks], [0., 1.], 'w-')
Expand All @@ -249,7 +249,7 @@ def init_figure(self, nightly, width=1920, height=1080, dpi=32):
# e.g., for no-gray mode, where we have only two programs.
continue
ax.set_xlim(-55, 293)
ax.set_ylim(-20, 77)
ax.set_ylim(-30, 77)
# Draw label for this plot.
pname = self.prognames[progidx]
pc = pcolors[pname]
Expand Down Expand Up @@ -553,7 +553,8 @@ def update(iframe):
animation = matplotlib.animation.FuncAnimation(
animator.figure, update, init_func=init, blit=True, frames=nframes)
writer = matplotlib.animation.writers['ffmpeg'](
bitrate=2400, fps=args.fps, metadata=dict(artist='surveymovie'))
bitrate=2400, fps=args.fps, codec='vp9',
metadata=dict(artist='surveymovie'))
save_name = args.save + '.mp4'
animation.save(save_name, writer=writer, dpi=animator.dpi)
log.info('Saved {0}.'.format(save_name))
16 changes: 11 additions & 5 deletions py/desisurvey/tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,17 @@ def allowed_in_conditions(self, cond):
res = res | (self.tileobsconditions == 'BRIGHT')
return res

def program_dependencies(self):
depends_on = dict()
config = desisurvey.config.Configuration()
for program in config.programs.keys:
prognode = getattr(config.programs, program)
dependencies = getattr(prognode, 'depends_on', None)
if dependencies is not None:
depends_on[program] = dependencies()
return depends_on


@property
def overlapping(self):
"""Dictionary of tile overlap matrices.
Expand Down Expand Up @@ -350,11 +361,6 @@ def _calculate_neighbors(self):
# ignore self matches
continue
self._neighbors[rownum[ind1]].append(rownum[ind2])

for passnum in np.unique(self.tilepass[mprogram]):
m = (mprogram & (self.tilepass == passnum) &
(self.in_desi != 0))
rownum = np.flatnonzero(m)
# self._neighbors: list of lists, giving tiles neighboring each
# tile

Expand Down

0 comments on commit 3c8e52c

Please sign in to comment.