From 21904240adbab63fa2cb585c994315021dadc09c Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 1 Aug 2016 14:51:20 -0700 Subject: [PATCH 01/11] tests for blocks, transitioner and non-schedulable. Fixed transitioner and scheduler errors --- astroplan/scheduling.py | 26 +++++++++-- astroplan/tests/test_scheduling.py | 72 +++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index 1080f2fe..60cbf97f 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -15,6 +15,7 @@ from astropy.table import Table from .utils import time_grid_from_range, stride_array +from .constraints import AltitudeConstraint __all__ = ['ObservingBlock', 'TransitionBlock', 'Schedule', 'Slot', 'Scheduler', 'SequentialScheduler', 'PriorityScheduler', 'Transitioner'] @@ -464,6 +465,12 @@ def _make_schedule(self, blocks): b._all_constraints = self.constraints else: b._all_constraints = self.constraints + b.constraints + # to make sure the scheduler has some constraint to work off of + # and to prevent scheduling of targets below the horizon + if b._all_constraints is None: + b._all_constraints = [AltitudeConstraint(min=0*u.deg)] + elif not any(isinstance(c, AltitudeConstraint) for c in b._all_constraints): + b._all_constraints.append(AltitudeConstraint(min=0*u.deg)) b._duration_offsets = u.Quantity([0*u.second, b.duration/2, b.duration]) b.observer = self.observer @@ -540,6 +547,12 @@ def _make_schedule(self, blocks): b._all_constraints = self.constraints else: b._all_constraints = self.constraints + b.constraints + # to make sure the scheduler has some constraint to work off of + # and to prevent scheduling of targets below the horizon + if b._all_constraints is None: + b._all_constraints = [AltitudeConstraint(min=0*u.deg)] + elif not any(isinstance(c, AltitudeConstraint) for c in b._all_constraints): + b._all_constraints.append(AltitudeConstraint(min=0*u.deg)) b._duration_offsets = u.Quantity([0 * u.second, b.duration / 2, b.duration]) _block_priorities[i] = b.priority _all_times.append(b.duration) @@ -687,7 +700,8 @@ def __init__(self, slew_rate=None, instrument_reconfig_times=None): If not None, gives a mapping from property names to another dictionary. The second dictionary maps 2-tuples of states to the time it takes to transition between those states (as an - `~astropy.units.Quantity`). + `~astropy.units.Quantity`), can also take a 'default' key + mapped to a default transition time. """ self.slew_rate = slew_rate self.instrument_reconfig_times = instrument_reconfig_times @@ -739,14 +753,20 @@ def __call__(self, oldblock, newblock, start_time, observer): def compute_instrument_transitions(self, oldblock, newblock): components = {} for conf_name, old_conf in oldblock.configuration.items(): - if conf_name in newblock: + if conf_name in newblock.configuration: conf_times = self.instrument_reconfig_times.get(conf_name, None) if conf_times is not None: - new_conf = newblock[conf_name] + new_conf = newblock.configuration[conf_name] ctime = conf_times.get((old_conf, new_conf), None) + def_time = conf_times.get('default', None) if ctime is not None: s = '{0}:{1} to {2}'.format(conf_name, old_conf, new_conf) components[s] = ctime + elif def_time and not old_conf == new_conf: + s = '{0}:{1} to {2}'.format(conf_name, old_conf, + new_conf) + components[s] = def_time + return components diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index b055ada1..2e6065f1 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -9,7 +9,7 @@ from ..observer import Observer from ..target import FixedTarget -from ..constraints import (AirmassConstraint, _get_altaz) +from ..constraints import (AirmassConstraint, AtNightConstraint, _get_altaz) from ..scheduling import (ObservingBlock, PriorityScheduler, SequentialScheduler, Transitioner, TransitionBlock, Schedule, Slot) @@ -22,26 +22,55 @@ apo = Observer.at_site('apo') targets = [vega, rigel, polaris] +default_time = Time('2016-02-06 00:00:00') +only_at_night = [AtNightConstraint()] + + +def test_observing_block(): + block = ObservingBlock(rigel, 1*u.minute, 0, configuration={'filter': 'b'}) + assert(block.configuration['filter'] == 'b') + assert(block.target == rigel) + times_per_exposure = [1*u.minute, 4*u.minute, 15*u.minute, 5*u.minute] + numbers_of_exposures = [100, 4, 3, 12] + readout_time = 0.5*u.minute + for index in range(len(times_per_exposure)): + block = ObservingBlock.from_exposures(vega, 0, times_per_exposure[index], + numbers_of_exposures[index], readout_time) + assert(block.duration == numbers_of_exposures[index] * + (times_per_exposure[index] + readout_time)) def test_transitioner(): blocks = [ObservingBlock(t, 55 * u.minute, i) for i, t in enumerate(targets)] slew_rate = 1 * u.deg / u.second trans = Transitioner(slew_rate=slew_rate) - start_time = Time('2016-02-06 00:00:00') + start_time = default_time transition = trans(blocks[0], blocks[1], start_time, apo) aaz = _get_altaz(Time([start_time]), apo, [blocks[0].target, blocks[1].target])['altaz'] sep = aaz[0].separation(aaz[1])[0] assert isinstance(transition, TransitionBlock) assert transition.duration == sep/slew_rate + blocks = [ObservingBlock(vega, 10*u.minute, 0, configuration={'filter': 'v'}), + ObservingBlock(vega, 10*u.minute, 0, configuration={'filter': 'i'}), + ObservingBlock(rigel, 10*u.minute, 0, configuration={'filter': 'i'})] + trans = Transitioner(slew_rate, instrument_reconfig_times={'filter': {('v', 'i'): 2*u.minute, + 'default': 5*u.minute}}) + transition1 = trans(blocks[0], blocks[1], start_time, apo) + transition2 = trans(blocks[0], blocks[2], start_time, apo) + transition3 = trans(blocks[1], blocks[0], start_time, apo) + assert np.abs(transition1.duration - 2*u.minute) < 1*u.second + assert np.abs(transition2.duration - 2*u.minute - transition.duration) < 1*u.second + # to test the default transition + assert np.abs(transition3.duration - 5*u.minute) < 1*u.second + transitioner = Transitioner(slew_rate=1 * u.deg / u.second) def test_priority_scheduler(): constraints = [AirmassConstraint(3, boolean_constraint=False)] blocks = [ObservingBlock(t, 55*u.minute, i) for i, t in enumerate(targets)] - start_time = Time('2016-02-06 00:00:00') + start_time = default_time end_time = start_time + 24*u.hour scheduler = PriorityScheduler(start_time, end_time, transitioner=transitioner, constraints=constraints, observer=apo) @@ -49,12 +78,13 @@ def test_priority_scheduler(): assert len(schedule.observing_blocks) == 3 assert all(np.abs(block.end_time - block.start_time - block.duration) < 1*u.second for block in schedule.scheduled_blocks) + assert None is None def test_sequential_scheduler(): constraints = [AirmassConstraint(2.5, boolean_constraint=False)] blocks = [ObservingBlock(t, 55 * u.minute, i) for i, t in enumerate(targets)] - start_time = Time('2016-02-06 00:00:00') + start_time = default_time end_time = start_time + 24 * u.hour scheduler = SequentialScheduler(start_time, end_time, constraints=constraints, observer=apo, @@ -65,8 +95,38 @@ def test_sequential_scheduler(): 1*u.second for block in schedule.scheduled_blocks) +def test_scheduling_target_down(): + lco = Observer.at_site('lco') + block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + start_time = default_time + end_time = start_time + 5*u.day + scheduler1 = SequentialScheduler(start_time, end_time, only_at_night, lco, + transitioner) + schedule1 = scheduler1(block) + assert len(schedule1.observing_blocks) == 0 + scheduler2 = PriorityScheduler(start_time, end_time, only_at_night, lco, + transitioner) + schedule2 = scheduler2(block) + assert len(schedule2.observing_blocks) == 0 + + +def test_scheduling_during_day(): + block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + day = default_time + start_time = apo.midnight(day) + 10*u.hour + end_time = start_time + 6*u.hour + scheduler1 = SequentialScheduler(start_time, end_time, only_at_night, apo, + transitioner) + schedule1 = scheduler1(block) + assert len(schedule1.observing_blocks) == 0 + scheduler2 = PriorityScheduler(start_time, end_time, only_at_night, apo, + transitioner) + schedule2 = scheduler2(block) + assert len(schedule2.observing_blocks) == 0 + + def test_slot(): - start_time = Time('2016-02-06 00:00:00') + start_time = default_time end_time = start_time + 24 * u.hour slot = Slot(start_time, end_time) slots = slot.split_slot(start_time, start_time+1*u.hour) @@ -75,7 +135,7 @@ def test_slot(): def test_schedule(): - start_time = Time('2016-02-06 00:00:00') + start_time = default_time end_time = start_time + 24 * u.hour schedule = Schedule(start_time, end_time) assert schedule.slots[0].start == start_time From b79743ff5f57c427c2cfd2b6197122d1ded1d6d8 Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 1 Aug 2016 19:58:22 -0700 Subject: [PATCH 02/11] additions to a few tests and fixing a flaw in a scheduler --- astroplan/scheduling.py | 5 ++-- astroplan/tests/test_scheduling.py | 41 ++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index 60cbf97f..b1c0449d 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -174,8 +174,6 @@ def __init__(self, start_time, end_time, constraints=None): self.end_time = end_time self.slots = [Slot(start_time, end_time)] self.constraints = constraints - self.slew_duration = 4*u.min - # TODO: replace/overwrite slew_duration with Transitioner calls self.observer = None def __repr__(self): @@ -617,8 +615,9 @@ def _make_schedule(self, blocks): # set duration such that the Block will fit in the strided array duration_indices = np.int(np.ceil(b.duration / time_resolution)) b.duration = duration_indices * time_resolution + # add 1 second to the start time to allow for scheduling at the start of a slot slot_index = [q for q, slot in enumerate(self.schedule.slots) - if slot.start < new_start_time < slot.end][0] + if slot.start < new_start_time + 1*u.second < slot.end][0] slots_before = self.schedule.slots[:slot_index] slots_after = self.schedule.slots[slot_index + 1:] # this has to remake transitions between already existing ObservingBlocks diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index 2e6065f1..506a5a5d 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -9,7 +9,8 @@ from ..observer import Observer from ..target import FixedTarget -from ..constraints import (AirmassConstraint, AtNightConstraint, _get_altaz) +from ..constraints import (AirmassConstraint, AtNightConstraint, _get_altaz, + MoonIlluminationConstraint) from ..scheduling import (ObservingBlock, PriorityScheduler, SequentialScheduler, Transitioner, TransitionBlock, Schedule, Slot) @@ -21,8 +22,8 @@ dec=89.26410897 * u.deg), name="Polaris") apo = Observer.at_site('apo') -targets = [vega, rigel, polaris] -default_time = Time('2016-02-06 00:00:00') +targets = [vega, polaris, rigel] +default_time = Time('2016-02-06 03:00:00') only_at_night = [AtNightConstraint()] @@ -45,9 +46,9 @@ def test_transitioner(): slew_rate = 1 * u.deg / u.second trans = Transitioner(slew_rate=slew_rate) start_time = default_time - transition = trans(blocks[0], blocks[1], start_time, apo) + transition = trans(blocks[0], blocks[2], start_time, apo) aaz = _get_altaz(Time([start_time]), apo, - [blocks[0].target, blocks[1].target])['altaz'] + [blocks[0].target, blocks[2].target])['altaz'] sep = aaz[0].separation(aaz[1])[0] assert isinstance(transition, TransitionBlock) assert transition.duration == sep/slew_rate @@ -71,21 +72,23 @@ def test_priority_scheduler(): constraints = [AirmassConstraint(3, boolean_constraint=False)] blocks = [ObservingBlock(t, 55*u.minute, i) for i, t in enumerate(targets)] start_time = default_time - end_time = start_time + 24*u.hour + end_time = start_time + 18*u.hour scheduler = PriorityScheduler(start_time, end_time, transitioner=transitioner, constraints=constraints, observer=apo) schedule = scheduler(blocks) assert len(schedule.observing_blocks) == 3 assert all(np.abs(block.end_time - block.start_time - block.duration) < 1*u.second for block in schedule.scheduled_blocks) - assert None is None + assert all([schedule.observing_blocks[0].target == polaris, + schedule.observing_blocks[1].target == rigel, + schedule.observing_blocks[2].target == vega]) def test_sequential_scheduler(): constraints = [AirmassConstraint(2.5, boolean_constraint=False)] blocks = [ObservingBlock(t, 55 * u.minute, i) for i, t in enumerate(targets)] start_time = default_time - end_time = start_time + 24 * u.hour + end_time = start_time + 18 * u.hour scheduler = SequentialScheduler(start_time, end_time, constraints=constraints, observer=apo, transitioner=transitioner) @@ -93,6 +96,11 @@ def test_sequential_scheduler(): assert len(schedule.observing_blocks) > 0 assert all(np.abs(block.end_time - block.start_time - block.duration) < 1*u.second for block in schedule.scheduled_blocks) + assert all([schedule.observing_blocks[0].target == rigel, + schedule.observing_blocks[1].target == polaris, + schedule.observing_blocks[2].target == vega]) + # vega rises late, so its start should be later + assert schedule.observing_blocks[2].start_time > start_time + 8*u.hour def test_scheduling_target_down(): @@ -124,6 +132,23 @@ def test_scheduling_during_day(): schedule2 = scheduler2(block) assert len(schedule2.observing_blocks) == 0 +''' +def test_scheduling_moon_up(): + block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + # on february 23 the moon was up between the start/end times defined below + day = default_time + 17 * u.day + start_time = apo.midnight(day) - 2 * u.hour + end_time = start_time + 6 * u.hour + constraints = [AtNightConstraint(), MoonIlluminationConstraint(max=0)] + scheduler1 = SequentialScheduler(start_time, end_time, constraints, apo, + transitioner) + schedule1 = scheduler1(block) + assert len(schedule1.observing_blocks) == 0 + scheduler2 = PriorityScheduler(start_time, end_time, constraints, apo, + transitioner) + schedule2 = scheduler2(block) + assert len(schedule2.observing_blocks) == 0 +''' def test_slot(): start_time = default_time From e540b6be32acfd4cdcddba12520e9b36e8396392 Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 2 Aug 2016 13:12:18 -0700 Subject: [PATCH 03/11] added some assertions --- astroplan/scheduling.py | 3 +-- astroplan/tests/test_scheduling.py | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index b1c0449d..13130ba7 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -144,8 +144,7 @@ def components(self, val): def from_duration(cls, duration): # for testing how to put transitions between observations during # scheduling without considering the complexities of duration - tb = TransitionBlock({None: 0*u.second}) - tb.duration = duration + tb = TransitionBlock({'duration': duration}) return tb diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index 506a5a5d..e715392b 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -64,6 +64,7 @@ def test_transitioner(): assert np.abs(transition2.duration - 2*u.minute - transition.duration) < 1*u.second # to test the default transition assert np.abs(transition3.duration - 5*u.minute) < 1*u.second + assert transition1.components is not None transitioner = Transitioner(slew_rate=1 * u.deg / u.second) @@ -82,6 +83,9 @@ def test_priority_scheduler(): assert all([schedule.observing_blocks[0].target == polaris, schedule.observing_blocks[1].target == rigel, schedule.observing_blocks[2].target == vega]) + # polaris and rigel both peak just before the start time + assert schedule.slots[0].block.target == polaris + assert schedule.slots[2].block.target == rigel def test_sequential_scheduler(): @@ -131,7 +135,7 @@ def test_scheduling_during_day(): transitioner) schedule2 = scheduler2(block) assert len(schedule2.observing_blocks) == 0 - +# bring this back when MoonIlluminationConstraint is working properly ''' def test_scheduling_moon_up(): block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] @@ -150,6 +154,7 @@ def test_scheduling_moon_up(): assert len(schedule2.observing_blocks) == 0 ''' + def test_slot(): start_time = default_time end_time = start_time + 24 * u.hour From 743ca2b3eb052d6e2c85858eb2cacae4c4f392bd Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 2 Aug 2016 14:50:36 -0700 Subject: [PATCH 04/11] moved _components=None to before components=components --- astroplan/scheduling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index 13130ba7..fd9ec116 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -107,10 +107,10 @@ def __init__(self, components, start_time=None): start_time : `~astropy.units.Quantity` Start time of observation """ + self._components = None self.duration = None self.start_time = start_time self.components = components - self._components = None def __repr__(self): orig_repr = object.__repr__(self) From 398956445532e99e1cc858caa33d5b232469fbf8 Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 2 Aug 2016 17:08:55 -0700 Subject: [PATCH 05/11] added tests for methods inside the Schedule --- astroplan/tests/test_scheduling.py | 75 ++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index e715392b..963e9672 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -41,6 +41,56 @@ def test_observing_block(): (times_per_exposure[index] + readout_time)) +def test_slot(): + start_time = default_time + end_time = start_time + 24 * u.hour + slot = Slot(start_time, end_time) + slots = slot.split_slot(start_time, start_time+1*u.hour) + assert len(slots) == 2 + assert slots[0].end == slots[1].start + + +def test_schedule(): + start_time = default_time + end_time = start_time + 24 * u.hour + schedule = Schedule(start_time, end_time) + assert schedule.slots[0].start == start_time + assert schedule.slots[0].end == end_time + assert schedule.slots[0].duration == 24*u.hour + schedule.new_slots(0, start_time, end_time) + assert len(schedule.slots) == 1 + new_slots = schedule.new_slots(0, start_time+1*u.hour, start_time+4*u.hour) + assert np.abs(new_slots[0].duration - 1*u.hour) < 1*u.second + assert np.abs(new_slots[1].duration - 3*u.hour) < 1*u.second + assert np.abs(new_slots[2].duration - 20*u.hour) < 1*u.second + + +def test_schedule_insert_slot(): + schedule = Schedule(default_time, default_time + 5*u.hour) + duration = 2*u.hour + 1*u.second + end_time = default_time + duration + block = TransitionBlock.from_duration(duration) + schedule.insert_slot(end_time - duration, block) + # when merged with blockgrouping, uncomment: + #assert schedule.slots[0]._old_block == block + # even when float evaluation doesn't work, it should still schedule properly + assert not end_time - duration == default_time + assert len(schedule.slots) == 2 + assert schedule.slots[0].start == default_time + + +def test_schedule_change_slot_block(): + schedule = Schedule(default_time, default_time + 5 * u.hour) + duration = 2 * u.hour + block = TransitionBlock.from_duration(duration) + schedule.insert_slot(default_time, block) + assert np.abs(schedule.slots[0].end - 2*u.hour - default_time) < 1*u.second + new_block = TransitionBlock.from_duration(1*u.minute) + schedule.change_slot_block(0, new_block) + assert np.abs(schedule.slots[0].end - 1*u.minute - default_time) < 1*u.second + assert schedule.slots[1].start == schedule.slots[0].end + + def test_transitioner(): blocks = [ObservingBlock(t, 55 * u.minute, i) for i, t in enumerate(targets)] slew_rate = 1 * u.deg / u.second @@ -153,28 +203,3 @@ def test_scheduling_moon_up(): schedule2 = scheduler2(block) assert len(schedule2.observing_blocks) == 0 ''' - - -def test_slot(): - start_time = default_time - end_time = start_time + 24 * u.hour - slot = Slot(start_time, end_time) - slots = slot.split_slot(start_time, start_time+1*u.hour) - assert len(slots) == 2 - assert slots[0].end == slots[1].start - - -def test_schedule(): - start_time = default_time - end_time = start_time + 24 * u.hour - schedule = Schedule(start_time, end_time) - assert schedule.slots[0].start == start_time - assert schedule.slots[0].end == end_time - assert schedule.slots[0].duration == 24*u.hour - schedule.new_slots(0, start_time, end_time) - assert len(schedule.slots) == 1 - new_slots = schedule.new_slots(0, start_time+1*u.hour, start_time+4*u.hour) - assert np.abs(new_slots[0].duration - 1*u.hour) < 1*u.second - assert np.abs(new_slots[1].duration - 3*u.hour) < 1*u.second - assert np.abs(new_slots[2].duration - 20*u.hour) < 1*u.second - From da4337affacefd460e0de707a972d67df02805f2 Mon Sep 17 00:00:00 2001 From: kvyh Date: Wed, 3 Aug 2016 11:34:24 -0700 Subject: [PATCH 06/11] changed np.abs to abs and changed line-break formatting --- astroplan/scheduling.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index fd9ec116..88d6e56a 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -176,9 +176,9 @@ def __init__(self, start_time, end_time, constraints=None): self.observer = None def __repr__(self): - return 'Schedule containing ' + str(len(self.observing_blocks)) + \ - ' observing blocks between ' + str(self.slots[0].start.iso) + \ - ' and ' + str(self.slots[-1].end.iso) + return ('Schedule containing ' + str(len(self.observing_blocks)) + + ' observing blocks between ' + str(self.slots[0].start.iso) + + ' and ' + str(self.slots[-1].end.iso)) def apply_constraints(self): # this needs to be able to handle being passed constraints @@ -241,8 +241,8 @@ def insert_slot(self, start_time, block): # due to float representation, this will change block start time # and duration by up to 1 second in order to fit in a slot for j, slot in enumerate(self.slots): - if (slot.start < start_time or np.abs(slot.start-start_time) < 1*u.second) \ - and (slot.end > start_time): + if ((slot.start < start_time or abs(slot.start-start_time) < 1*u.second) + and (slot.end > start_time)): slot_index = j if (block.duration - self.slots[slot_index].duration) > 1*u.second: print(self.slots[slot_index].duration.to(u.second), block.duration) @@ -250,15 +250,16 @@ def insert_slot(self, start_time, block): elif self.slots[slot_index].end - block.duration < start_time: start_time = self.slots[slot_index].end - block.duration - if np.abs((self.slots[slot_index].duration - block.duration) < 1 * u.second): + if abs((self.slots[slot_index].duration - block.duration) < 1 * u.second): block.duration = self.slots[slot_index].duration start_time = self.slots[slot_index].start end_time = self.slots[slot_index].end - elif np.abs(self.slots[slot_index].start - start_time) < 1*u.second: + elif abs(self.slots[slot_index].start - start_time) < 1*u.second: start_time = self.slots[slot_index].start end_time = start_time + block.duration - elif np.abs(self.slots[slot_index].end - start_time - block.duration) < 1*u.second: + elif abs(self.slots[slot_index].end - start_time - block.duration) < 1*u.second: end_time = self.slots[slot_index].end + else: end_time = start_time + block.duration if isinstance(block, ObservingBlock): @@ -631,8 +632,8 @@ def _make_schedule(self, blocks): end_idx = times_indices + start_idx # this may make some OBs get sub-optimal scheduling, but it closes gaps # TODO: determine a reasonable range inside which it gets shifted - if tb.duration > new_start_time - tb.start_time or \ - np.abs(new_start_time - tb.end_time) < self.gap_time: + if (tb.duration > new_start_time - tb.start_time or + abs(new_start_time - tb.end_time) < self.gap_time): new_start_time = tb.end_time start_time_idx = end_idx self.schedule.insert_slot(tb.start_time, tb) @@ -648,8 +649,8 @@ def _make_schedule(self, blocks): start_idx = self.schedule.slots[slot_index - 2].block.end_idx end_idx = times_indices + start_idx self.schedule.change_slot_block(slot_index - 1, new_block=tb) - if tb.duration > new_start_time - tb.start_time or \ - np.abs(new_start_time - tb.end_time) < self.gap_time: + if (tb.duration > new_start_time - tb.start_time or + abs(new_start_time - tb.end_time) < self.gap_time): new_start_time = tb.end_time start_time_idx = end_idx is_open_time[start_idx: end_idx] = False From 0f0f14a9974bf838de8eed42da6abdfd1f3adeb2 Mon Sep 17 00:00:00 2001 From: kvyh Date: Wed, 3 Aug 2016 11:35:00 -0700 Subject: [PATCH 07/11] removed extra line --- astroplan/scheduling.py | 1 - 1 file changed, 1 deletion(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index 88d6e56a..fc3812d4 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -259,7 +259,6 @@ def insert_slot(self, start_time, block): end_time = start_time + block.duration elif abs(self.slots[slot_index].end - start_time - block.duration) < 1*u.second: end_time = self.slots[slot_index].end - else: end_time = start_time + block.duration if isinstance(block, ObservingBlock): From e3002bd4a286b4a9fd06535289d34f48efe256e1 Mon Sep 17 00:00:00 2001 From: kvyh Date: Thu, 4 Aug 2016 16:28:39 -0700 Subject: [PATCH 08/11] fixed an issue with rounding Quantities --- astroplan/scheduling.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index fc3812d4..85a9accf 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -242,7 +242,7 @@ def insert_slot(self, start_time, block): # and duration by up to 1 second in order to fit in a slot for j, slot in enumerate(self.slots): if ((slot.start < start_time or abs(slot.start-start_time) < 1*u.second) - and (slot.end > start_time)): + and (slot.end > start_time + 1*u.second)): slot_index = j if (block.duration - self.slots[slot_index].duration) > 1*u.second: print(self.slots[slot_index].duration.to(u.second), block.duration) @@ -583,11 +583,15 @@ def _make_schedule(self, blocks): # And then remove any times that are already scheduled constraint_scores[is_open_time == False] = 0 # Select the most optimal time + # need to leave time around the Block for transitions + max_config_time = sum([max(value.values()) for value in + self.transitioner.instrument_reconfig_times.values()]) + buffer_time = (160*u.deg/self.transitioner.slew_rate + max_config_time) # TODO: make it so that this isn't required to prevent errors in slot creation - total_duration = b.duration + self.gap_time + total_duration = b.duration + buffer_time # calculate the number of time slots needed for this exposure - _stride_by = np.int(np.ceil(total_duration / time_resolution)) + _stride_by = np.int(np.ceil(float(total_duration / time_resolution))) # Stride the score arrays by that number _strided_scores = stride_array(constraint_scores, _stride_by) @@ -612,7 +616,7 @@ def _make_schedule(self, blocks): if _is_scheduled: # set duration such that the Block will fit in the strided array - duration_indices = np.int(np.ceil(b.duration / time_resolution)) + duration_indices = np.int(np.ceil(float(b.duration / time_resolution))) b.duration = duration_indices * time_resolution # add 1 second to the start time to allow for scheduling at the start of a slot slot_index = [q for q, slot in enumerate(self.schedule.slots) @@ -625,7 +629,7 @@ def _make_schedule(self, blocks): # make a transition object after the previous ObservingBlock tb = self.transitioner(self.schedule.slots[slot_index - 1].block, b, self.schedule.slots[slot_index - 1].end, self.observer) - times_indices = np.int(np.ceil(tb.duration / time_resolution)) + times_indices = np.int(np.ceil(float(tb.duration / time_resolution))) tb.duration = times_indices * time_resolution start_idx = self.schedule.slots[slot_index - 1].block.end_idx end_idx = times_indices + start_idx @@ -643,7 +647,7 @@ def _make_schedule(self, blocks): # change the existing TransitionBlock to what it needs to be now tb = self.transitioner(self.schedule.slots[slot_index - 2].block, b, self.schedule.slots[slot_index - 2].end, self.observer) - times_indices = np.int(np.ceil(tb.duration / time_resolution)) + times_indices = np.int(np.ceil(float(tb.duration / time_resolution))) tb.duration = times_indices * time_resolution start_idx = self.schedule.slots[slot_index - 2].block.end_idx end_idx = times_indices + start_idx @@ -660,7 +664,7 @@ def _make_schedule(self, blocks): # make a transition object after the new ObservingBlock tb = self.transitioner(b, self.schedule.slots[slot_index + 1].block, new_start_time + b.duration, self.observer) - times_indices = np.int(np.ceil(tb.duration / time_resolution)) + times_indices = np.int(np.ceil(float(tb.duration / time_resolution))) tb.duration = times_indices * time_resolution self.schedule.insert_slot(tb.start_time, tb) start_idx = end_time_idx From 1e9b7de62dfdfabbc4928d31bb600591860b5be7 Mon Sep 17 00:00:00 2001 From: kvyh Date: Thu, 4 Aug 2016 23:02:22 -0700 Subject: [PATCH 09/11] fixed a few issues that can occur because of the Transitioner --- astroplan/scheduling.py | 14 ++++++++++---- astroplan/tests/test_scheduling.py | 2 -- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/astroplan/scheduling.py b/astroplan/scheduling.py index 85a9accf..7a29f04d 100644 --- a/astroplan/scheduling.py +++ b/astroplan/scheduling.py @@ -585,9 +585,15 @@ def _make_schedule(self, blocks): # Select the most optimal time # need to leave time around the Block for transitions - max_config_time = sum([max(value.values()) for value in - self.transitioner.instrument_reconfig_times.values()]) - buffer_time = (160*u.deg/self.transitioner.slew_rate + max_config_time) + if self.transitioner.instrument_reconfig_times: + max_config_time = sum([max(value.values()) for value in + self.transitioner.instrument_reconfig_times.values()]) + else: + max_config_time = 0*u.second + if self.transitioner.slew_rate: + buffer_time = (160*u.deg/self.transitioner.slew_rate + max_config_time) + else: + buffer_time = max_config_time # TODO: make it so that this isn't required to prevent errors in slot creation total_duration = b.duration + buffer_time # calculate the number of time slots needed for this exposure @@ -750,7 +756,7 @@ def __call__(self, oldblock, newblock, start_time, observer): if components: return TransitionBlock(components, start_time) else: - return None + return TransitionBlock.from_duration(0*u.second) def compute_instrument_transitions(self, oldblock, newblock): components = {} diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index 963e9672..ecb4d2ef 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -71,8 +71,6 @@ def test_schedule_insert_slot(): end_time = default_time + duration block = TransitionBlock.from_duration(duration) schedule.insert_slot(end_time - duration, block) - # when merged with blockgrouping, uncomment: - #assert schedule.slots[0]._old_block == block # even when float evaluation doesn't work, it should still schedule properly assert not end_time - duration == default_time assert len(schedule.slots) == 2 From 85a55ee7462ac90dbda064f0b3460fab6ff73493 Mon Sep 17 00:00:00 2001 From: kvyh Date: Fri, 5 Aug 2016 01:55:09 -0700 Subject: [PATCH 10/11] modified a couple of tests and activated the test moon up --- astroplan/tests/test_scheduling.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/astroplan/tests/test_scheduling.py b/astroplan/tests/test_scheduling.py index ecb4d2ef..f5ed8630 100644 --- a/astroplan/tests/test_scheduling.py +++ b/astroplan/tests/test_scheduling.py @@ -157,9 +157,9 @@ def test_sequential_scheduler(): def test_scheduling_target_down(): lco = Observer.at_site('lco') - block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + block = [ObservingBlock(FixedTarget.from_name('polaris'), 20 * u.min, 0)] start_time = default_time - end_time = start_time + 5*u.day + end_time = start_time + 2*u.day scheduler1 = SequentialScheduler(start_time, end_time, only_at_night, lco, transitioner) schedule1 = scheduler1(block) @@ -171,7 +171,7 @@ def test_scheduling_target_down(): def test_scheduling_during_day(): - block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + block = [ObservingBlock(FixedTarget.from_name('polaris'), 20 * u.min, 0)] day = default_time start_time = apo.midnight(day) + 10*u.hour end_time = start_time + 6*u.hour @@ -183,21 +183,20 @@ def test_scheduling_during_day(): transitioner) schedule2 = scheduler2(block) assert len(schedule2.observing_blocks) == 0 -# bring this back when MoonIlluminationConstraint is working properly -''' + + def test_scheduling_moon_up(): - block = [ObservingBlock(FixedTarget.from_name('polaris'), 1 * u.min, 0)] + block = [ObservingBlock(FixedTarget.from_name('polaris'), 20 * u.min, 0)] # on february 23 the moon was up between the start/end times defined below day = default_time + 17 * u.day start_time = apo.midnight(day) - 2 * u.hour end_time = start_time + 6 * u.hour constraints = [AtNightConstraint(), MoonIlluminationConstraint(max=0)] - scheduler1 = SequentialScheduler(start_time, end_time, constraints, apo, - transitioner) + scheduler1 = PriorityScheduler(start_time, end_time, constraints, apo, + transitioner) schedule1 = scheduler1(block) assert len(schedule1.observing_blocks) == 0 - scheduler2 = PriorityScheduler(start_time, end_time, constraints, apo, - transitioner) + scheduler2 = SequentialScheduler(start_time, end_time, constraints, apo, + transitioner) schedule2 = scheduler2(block) assert len(schedule2.observing_blocks) == 0 -''' From dc2b7dddd2a082593e49ade398fbe693ba3cbc88 Mon Sep 17 00:00:00 2001 From: kvyh Date: Fri, 5 Aug 2016 01:56:30 -0700 Subject: [PATCH 11/11] moved moon_altaz to the non-location get_moon --- astroplan/observer.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/astroplan/observer.py b/astroplan/observer.py index 06c511ae..170db79e 100644 --- a/astroplan/observer.py +++ b/astroplan/observer.py @@ -1487,15 +1487,12 @@ def moon_altaz(self, time, ephemeris=None): return moon else: - moon_coords = [] - for t in time: - altaz_frame = AltAz(location=self.location, obstime=t) - moon_coord = get_moon(t, location=self.location, ephemeris=ephemeris).transform_to(altaz_frame) - moon_coords.append(moon_coord) - obstime = [coord.obstime for coord in moon_coords] - alts = u.Quantity([coord.alt for coord in moon_coords]) - azs = u.Quantity([coord.az for coord in moon_coords]) - dists = u.Quantity([coord.distance for coord in moon_coords]) + altaz_frame = AltAz(location=self.location, obstime=time) + moon_coords = get_moon(time, ephemeris=ephemeris).transform_to(altaz_frame) + obstime = time + alts = moon_coords.alt + azs = moon_coords.az + dists = moon_coords.distance return SkyCoord(AltAz(azs, alts, dists, obstime=obstime, location=self.location)) @u.quantity_input(horizon=u.deg)