Skip to content

Commit

Permalink
Merge pull request #497 from gemini-hlsw/SCHED-724
Browse files Browse the repository at this point in the history
SCHED-724: Separate time losses by type
  • Loading branch information
stroncod committed Aug 22, 2024
2 parents 6f30dc2 + 088a079 commit 3e75719
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 6 deletions.
4 changes: 3 additions & 1 deletion scheduler/core/plans/nightstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
'NightStats',
]

from scheduler.core.types import TimeLossType


@final
@dataclass(frozen=True)
class NightStats:
time_loss: str
time_loss: Dict[TimeLossType, int]
plan_score: float
n_toos: int
completion_fraction: Dict[Band, int]
Expand Down
42 changes: 40 additions & 2 deletions scheduler/core/statscalculator/statscalculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
from lucupy.types import ZeroTime

from scheduler.core.components.collector import Collector
from scheduler.core.eventsqueue import InterruptionResolutionEvent, FaultResolutionEvent, WeatherClosureResolutionEvent, \
InterruptionEvent, FaultEvent, WeatherClosureEvent
from scheduler.core.eventsqueue.nightchanges import NightlyTimeline
from scheduler.core.plans import NightStats
from scheduler.core.types import TimeLossType
from scheduler.graphql_mid.scalars import Sites
from scheduler.services import logger_factory

Expand All @@ -27,6 +30,11 @@ class StatCalculator:
Interface for stats in the calculation and results of plans.
"""

_UNSCHEDULE_KEY: TimeLossType = 'unschedule'
_WEATHER_KEY: TimeLossType = 'weather'
_FAULT_KEY: TimeLossType = 'faults'


@staticmethod
def program_real_total_used(program: Program):
return sum((o.part_time() + o.acq_overhead + o.prog_time() for o in program.observations()),
Expand All @@ -42,12 +50,43 @@ def calculate_timeline_stats(timeline: NightlyTimeline,
programs = {}
for night_idx in nights:
for site in sites:
time_losses = {StatCalculator._FAULT_KEY: 0,
StatCalculator._WEATHER_KEY: 0,
StatCalculator._UNSCHEDULE_KEY: 0}
# Gather unsolved interruptions during the night.
interruptions = []
for entry in timeline.timeline[night_idx][site]:
if isinstance(entry.event, InterruptionEvent):
interruptions.append(entry.event)
elif isinstance(entry.event, InterruptionResolutionEvent):
interruptions.pop()

for e in interruptions:
if isinstance(e, FaultEvent):
time_loss = timeline.timeline[night_idx][site][-1].event.time - e.time
time_losses[StatCalculator._FAULT_KEY] += time_loss

elif isinstance(e, WeatherClosureEvent):
time_loss = timeline.timeline[night_idx][site][-1].event.time - e.time
time_losses[StatCalculator._WEATHER_KEY] += time_loss

for entry in timeline.timeline[night_idx][site]:
# Morning twilight generates no plan.
if entry.plan_generated is None:
continue

plan = entry.plan_generated # Update last plan

if isinstance(entry.event, InterruptionResolutionEvent):
if isinstance(entry.event, FaultResolutionEvent):
time_losses[StatCalculator._FAULT_KEY] += int(entry.event.time_loss.total_seconds()/60)
elif isinstance(entry.event, WeatherClosureResolutionEvent):
time_losses[StatCalculator._WEATHER_KEY] += int(entry.event.time_loss.total_seconds()/60)

time_losses[StatCalculator._UNSCHEDULE_KEY] = (plan.time_left() -
time_losses[StatCalculator._FAULT_KEY] -
time_losses[StatCalculator._WEATHER_KEY])

n_toos = 0
plan_score = 0
plan_conditions = []
Expand Down Expand Up @@ -81,7 +120,7 @@ def calculate_timeline_stats(timeline: NightlyTimeline,

program_completion = {p.id: StatCalculator.calculate_program_completion(programs[p])
for p in programs}
plan.night_stats = NightStats(f'{plan.time_left()} min',
plan.night_stats = NightStats(time_losses,
plan_score,
n_toos,
completion_fraction,
Expand All @@ -95,7 +134,6 @@ def calculate_timeline_stats(timeline: NightlyTimeline,

completion = f'{float(total_used.total_seconds() / prog_total.total_seconds()) * 100:.1f}%'
score = scores_per_program[p_id]
# print(completion, score)
plans_summary[p_id.id] = (completion, score)

return plans_summary
Expand Down
1 change: 1 addition & 0 deletions scheduler/core/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
# The scores for all time slots prior to the night's time slot will be set to zero to allow for partial night scoring.
# Cannot include in Selector since this introduces a circular dependency.
StartingTimeslots: TypeAlias = Dict[Site, Dict[NightIndex, TimeslotIndex]]
TimeLossType: TypeAlias = str
5 changes: 3 additions & 2 deletions scheduler/graphql_mid/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
class SNightStats:
"""Night stats to display in the UI
"""
timeloss: str
time_loss: JSON
plan_score: float
n_toos: int
completion_fraction: JSON
Expand All @@ -34,7 +34,8 @@ class SNightStats:
def from_computed_night_stats(ns: NightStats) -> 'SNightStats':
cf = json.dumps(ns.completion_fraction)
pc = json.dumps(ns.program_completion)
return SNightStats(timeloss=ns.time_loss,
tl = json.dumps(ns.time_loss)
return SNightStats(time_loss=tl,
plan_score=ns.plan_score,
n_toos=ns.n_toos,
completion_fraction=cf,
Expand Down
5 changes: 5 additions & 0 deletions scheduler/scripts/run_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from scheduler.core.plans import Plans
from scheduler.core.eventsqueue import EveningTwilightEvent, Event, EventQueue, MorningTwilightEvent, WeatherChangeEvent
from scheduler.core.sources.sources import Sources
from scheduler.core.statscalculator import StatCalculator
from scheduler.services import logger_factory


Expand Down Expand Up @@ -394,6 +395,10 @@ def main(*,
final_plans[site] = nightly_timeline.get_final_plan(NightIndex(night_idx), site)
overall_plans[night_idx] = final_plans

plan_summary = StatCalculator.calculate_timeline_stats(nightly_timeline,
night_indices,
sites, collector)

# Make sure we have a list of the final plans sorted by night index.
plans_list = [p for _, p in sorted(overall_plans.items())]
# plan_summary = StatCalculator.calculate_plans_stats(overall_plans, collector)
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/scheduler/graphql_mid/test_graphql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async def test_schedule_query():
obsId
},
nightStats{
timeloss
timeLoss
}
}
}
Expand Down

0 comments on commit 3e75719

Please sign in to comment.