Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions src/virtualship/cli/_run.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""do_expedition function."""

import glob
import logging
import os
import shutil
Expand All @@ -14,11 +15,12 @@
ScheduleProblem,
simulate_schedule,
)
from virtualship.models import Schedule
from virtualship.models.checkpoint import Checkpoint
from virtualship.make_realistic.problems.simulator import ProblemSimulator
from virtualship.models import Checkpoint, Schedule
from virtualship.utils import (
CHECKPOINT,
_get_expedition,
_save_checkpoint,
expedition_cost,
get_instrument_class,
)
Expand All @@ -35,7 +37,10 @@
logging.getLogger("copernicusmarine").setLevel("ERROR")


def _run(expedition_dir: str | Path, from_data: Path | None = None) -> None:
# TODO: prob-level needs to be parsed from CLI args; currently set to 1 override for testing purposes
def _run(
expedition_dir: str | Path, from_data: Path | None = None, prob_level: int = 1
) -> None:
"""
Perform an expedition, providing terminal feedback and file output.

Expand Down Expand Up @@ -73,7 +78,7 @@ def _run(expedition_dir: str | Path, from_data: Path | None = None) -> None:

expedition = _get_expedition(expedition_dir)

# Verify instruments_config file is consistent with schedule
# verify instruments_config file is consistent with schedule
expedition.instruments_config.verify(expedition)

# load last checkpoint
Expand All @@ -82,7 +87,7 @@ def _run(expedition_dir: str | Path, from_data: Path | None = None) -> None:
checkpoint = Checkpoint(past_schedule=Schedule(waypoints=[]))

# verify that schedule and checkpoint match
checkpoint.verify(expedition.schedule)
checkpoint.verify(expedition.schedule, expedition_dir)

print("\n---- WAYPOINT VERIFICATION ----")

Expand All @@ -96,6 +101,8 @@ def _run(expedition_dir: str | Path, from_data: Path | None = None) -> None:
projection=projection,
expedition=expedition,
)

# handle cases where user defined schedule is incompatible (i.e. not enough time between waypoints, not problems)
if isinstance(schedule_results, ScheduleProblem):
print(
f"SIMULATION PAUSED: update your schedule (`virtualship plan`) and continue the expedition by executing the `virtualship run` command again.\nCheckpoint has been saved to {expedition_dir.joinpath(CHECKPOINT)}."
Expand Down Expand Up @@ -124,12 +131,29 @@ def _run(expedition_dir: str | Path, from_data: Path | None = None) -> None:

print("\n--- MEASUREMENT SIMULATIONS ---")

# identify problems
# TODO: prob_level needs to be parsed from CLI args
problem_simulator = ProblemSimulator(
expedition.schedule, prob_level, expedition_dir
)
problems = problem_simulator.select_problems()

# simulate measurements
print("\nSimulating measurements. This may take a while...\n")

# TODO: logic for getting simulations to carry on from last checkpoint! Building on .zarr files already created...

instruments_in_expedition = expedition.get_instruments()

for itype in instruments_in_expedition:
for i, itype in enumerate(instruments_in_expedition):
# propagate problems (pre-departure problems will only occur in first iteration)
if problems:
problem_simulator.execute(
problems=problems,
pre_departure=True if i == 0 else False,
instrument_type=itype,
)

# get instrument class
instrument_class = get_instrument_class(itype)
if instrument_class is None:
Expand Down Expand Up @@ -174,9 +198,13 @@ def _load_checkpoint(expedition_dir: Path) -> Checkpoint | None:
return None


def _save_checkpoint(checkpoint: Checkpoint, expedition_dir: Path) -> None:
file_path = expedition_dir.joinpath(CHECKPOINT)
checkpoint.to_yaml(file_path)
def _load_hashes(expedition_dir: Path) -> set[str]:
hashes_path = expedition_dir.joinpath("problems_encountered")
if not hashes_path.exists():
return set()
hash_files = glob.glob(str(hashes_path / "problem_*.txt"))
hashes = {Path(f).stem.split("_")[1] for f in hash_files}
return hashes


def _write_expedition_cost(expedition, schedule_results, expedition_dir):
Expand Down
8 changes: 6 additions & 2 deletions src/virtualship/expedition/simulate_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import ClassVar
from typing import TYPE_CHECKING, ClassVar

import pyproj

Expand All @@ -21,6 +21,9 @@
Waypoint,
)

if TYPE_CHECKING:
pass


@dataclass
class ScheduleOk:
Expand Down Expand Up @@ -124,7 +127,8 @@ def simulate(self) -> ScheduleOk | ScheduleProblem:
print(
f"Waypoint {wp_i + 1} could not be reached in time. Current time: {self._time}. Waypoint time: {waypoint.time}."
"\n\nHave you ensured that your schedule includes sufficient time for taking measurements, e.g. CTD casts (in addition to the time it takes to sail between waypoints)?\n"
"**Note**, the `virtualship plan` tool will not account for measurement times when verifying the schedule, only the time it takes to sail between waypoints.\n"
"**Hint #1**, the `virtualship plan` tool will not account for measurement times when verifying the schedule, only the time it takes to sail between waypoints.\n"
"**Hint #2**: if you previously encountered any unforeseen delays (e.g. equipment failure, pre-departure delays) during your expedition, you will need to adjust the timings of **all** waypoints after the affected waypoint, not just the next one."
)
return ScheduleProblem(self._time, wp_i)
else:
Expand Down
Loading