From 63cbda5f756384311c32cbd03a2ec77d975cebc8 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:01:36 +0100 Subject: [PATCH 01/56] first refactoring step, parent instrument classes --- src/virtualship/cli/_fetch.py | 11 ++ src/virtualship/instruments/adcp.py | 78 -------- src/virtualship/instruments/argo_float.py | 186 ------------------ src/virtualship/instruments/ctd.py | 164 ++++----------- src/virtualship/instruments/drifter.py | 113 ----------- .../instruments/ship_underwater_st.py | 76 ------- src/virtualship/instruments/xbt.py | 141 ------------- src/virtualship/models/instruments.py | 82 ++++++++ 8 files changed, 135 insertions(+), 716 deletions(-) delete mode 100644 src/virtualship/instruments/adcp.py delete mode 100644 src/virtualship/instruments/argo_float.py delete mode 100644 src/virtualship/instruments/drifter.py delete mode 100644 src/virtualship/instruments/ship_underwater_st.py delete mode 100644 src/virtualship/instruments/xbt.py create mode 100644 src/virtualship/models/instruments.py diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 60008304..bf35fe2f 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -86,6 +86,17 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None ) shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) + #! + #### TODO + # ++ new logic here where iterates (?) through available instruments and determines whether download is required: + # ++ by conditions of: + # 1) whether it's in the schedule (and from this be able to call the right classes from the instruments directory?) and + #! 2) is there a clever way of not unnecessarily duplicating data downloads if instruments use the same?! + # (try with a version first where does them all in tow and then try and optimise...?) + + #! + ## TODO: move to generic bathymetry download which is done for all expeditions + if ( ( {"XBT", "CTD", "CDT_BGC", "SHIP_UNDERWATER_ST"} diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py deleted file mode 100644 index af2c285e..00000000 --- a/src/virtualship/instruments/adcp.py +++ /dev/null @@ -1,78 +0,0 @@ -"""ADCP instrument.""" - -from pathlib import Path - -import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable - -from virtualship.models import Spacetime - -# we specifically use ScipyParticle because we have many small calls to execute -# there is some overhead with JITParticle and this ends up being significantly faster -_ADCPParticle = ScipyParticle.add_variables( - [ - Variable("U", dtype=np.float32, initial=np.nan), - Variable("V", dtype=np.float32, initial=np.nan), - ] -) - - -def _sample_velocity(particle, fieldset, time): - particle.U, particle.V = fieldset.UV.eval( - time, particle.depth, particle.lat, particle.lon, applyConversion=False - ) - - -def simulate_adcp( - fieldset: FieldSet, - out_path: str | Path, - max_depth: float, - min_depth: float, - num_bins: int, - sample_points: list[Spacetime], -) -> None: - """ - Use Parcels to simulate an ADCP in a fieldset. - - :param fieldset: The fieldset to simulate the ADCP in. - :param out_path: The path to write the results to. - :param max_depth: Maximum depth the ADCP can measure. - :param min_depth: Minimum depth the ADCP can measure. - :param num_bins: How many samples to take in the complete range between max_depth and min_depth. - :param sample_points: The places and times to sample at. - """ - sample_points.sort(key=lambda p: p.time) - - bins = np.linspace(max_depth, min_depth, num_bins) - num_particles = len(bins) - particleset = ParticleSet.from_list( - fieldset=fieldset, - pclass=_ADCPParticle, - lon=np.full( - num_particles, 0.0 - ), # initial lat/lon are irrelevant and will be overruled later. - lat=np.full(num_particles, 0.0), - depth=bins, - time=0, # same for time - ) - - # define output file for the simulation - # outputdt set to infinite as we just want to write at the end of every call to 'execute' - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - - for point in sample_points: - particleset.lon_nextloop[:] = point.location.lon - particleset.lat_nextloop[:] = point.location.lat - particleset.time_nextloop[:] = fieldset.time_origin.reltime( - np.datetime64(point.time) - ) - - # perform one step using the particleset - # dt and runtime are set so exactly one step is made. - particleset.execute( - [_sample_velocity], - dt=1, - runtime=1, - verbose_progress=False, - output_file=out_file, - ) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py deleted file mode 100644 index d0976367..00000000 --- a/src/virtualship/instruments/argo_float.py +++ /dev/null @@ -1,186 +0,0 @@ -"""Argo float instrument.""" - -import math -from dataclasses import dataclass -from datetime import datetime, timedelta -from pathlib import Path - -import numpy as np -from parcels import ( - AdvectionRK4, - FieldSet, - JITParticle, - ParticleSet, - StatusCode, - Variable, -) - -from virtualship.models import Spacetime - - -@dataclass -class ArgoFloat: - """Configuration for a single Argo float.""" - - spacetime: Spacetime - min_depth: float - max_depth: float - drift_depth: float - vertical_speed: float - cycle_days: float - drift_days: float - - -_ArgoParticle = JITParticle.add_variables( - [ - Variable("cycle_phase", dtype=np.int32, initial=0.0), - Variable("cycle_age", dtype=np.float32, initial=0.0), - Variable("drift_age", dtype=np.float32, initial=0.0), - Variable("salinity", dtype=np.float32, initial=np.nan), - Variable("temperature", dtype=np.float32, initial=np.nan), - Variable("min_depth", dtype=np.float32), - Variable("max_depth", dtype=np.float32), - Variable("drift_depth", dtype=np.float32), - Variable("vertical_speed", dtype=np.float32), - Variable("cycle_days", dtype=np.int32), - Variable("drift_days", dtype=np.int32), - ] -) - - -def _argo_float_vertical_movement(particle, fieldset, time): - if particle.cycle_phase == 0: - # Phase 0: Sinking with vertical_speed until depth is drift_depth - particle_ddepth += ( # noqa Parcels defines particle_* variables, which code checkers cannot know. - particle.vertical_speed * particle.dt - ) - if particle.depth + particle_ddepth <= particle.drift_depth: - particle_ddepth = particle.drift_depth - particle.depth - particle.cycle_phase = 1 - - elif particle.cycle_phase == 1: - # Phase 1: Drifting at depth for drifttime seconds - particle.drift_age += particle.dt - if particle.drift_age >= particle.drift_days * 86400: - particle.drift_age = 0 # reset drift_age for next cycle - particle.cycle_phase = 2 - - elif particle.cycle_phase == 2: - # Phase 2: Sinking further to max_depth - particle_ddepth += particle.vertical_speed * particle.dt - if particle.depth + particle_ddepth <= particle.max_depth: - particle_ddepth = particle.max_depth - particle.depth - particle.cycle_phase = 3 - - elif particle.cycle_phase == 3: - # Phase 3: Rising with vertical_speed until at surface - particle_ddepth -= particle.vertical_speed * particle.dt - particle.cycle_age += ( - particle.dt - ) # solve issue of not updating cycle_age during ascent - if particle.depth + particle_ddepth >= particle.min_depth: - particle_ddepth = particle.min_depth - particle.depth - particle.temperature = ( - math.nan - ) # reset temperature to NaN at end of sampling cycle - particle.salinity = math.nan # idem - particle.cycle_phase = 4 - else: - particle.temperature = fieldset.T[ - time, particle.depth, particle.lat, particle.lon - ] - particle.salinity = fieldset.S[ - time, particle.depth, particle.lat, particle.lon - ] - - elif particle.cycle_phase == 4: - # Phase 4: Transmitting at surface until cycletime is reached - if particle.cycle_age > particle.cycle_days * 86400: - particle.cycle_phase = 0 - particle.cycle_age = 0 - - if particle.state == StatusCode.Evaluate: - particle.cycle_age += particle.dt # update cycle_age - - -def _keep_at_surface(particle, fieldset, time): - # Prevent error when float reaches surface - if particle.state == StatusCode.ErrorThroughSurface: - particle.depth = particle.min_depth - particle.state = StatusCode.Success - - -def _check_error(particle, fieldset, time): - if particle.state >= 50: # This captures all Errors - particle.delete() - - -def simulate_argo_floats( - fieldset: FieldSet, - out_path: str | Path, - argo_floats: list[ArgoFloat], - outputdt: timedelta, - endtime: datetime | None, -) -> None: - """ - Use Parcels to simulate a set of Argo floats in a fieldset. - - :param fieldset: The fieldset to simulate the Argo floats in. - :param out_path: The path to write the results to. - :param argo_floats: A list of Argo floats to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :param endtime: Stop at this time, or if None, continue until the end of the fieldset. - """ - DT = 10.0 # dt of Argo float simulation integrator - - if len(argo_floats) == 0: - print( - "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - # define parcel particles - argo_float_particleset = ParticleSet( - fieldset=fieldset, - pclass=_ArgoParticle, - lat=[argo.spacetime.location.lat for argo in argo_floats], - lon=[argo.spacetime.location.lon for argo in argo_floats], - depth=[argo.min_depth for argo in argo_floats], - time=[argo.spacetime.time for argo in argo_floats], - min_depth=[argo.min_depth for argo in argo_floats], - max_depth=[argo.max_depth for argo in argo_floats], - drift_depth=[argo.drift_depth for argo in argo_floats], - vertical_speed=[argo.vertical_speed for argo in argo_floats], - cycle_days=[argo.cycle_days for argo in argo_floats], - drift_days=[argo.drift_days for argo in argo_floats], - ) - - # define output file for the simulation - out_file = argo_float_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(argo_float_particleset), 100] - ) - - # get earliest between fieldset end time and provide end time - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: - actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: - print("WARN: Requested end time later than fieldset end time.") - actual_endtime = fieldset_endtime - else: - actual_endtime = np.timedelta64(endtime) - - # execute simulation - argo_float_particleset.execute( - [ - _argo_float_vertical_movement, - AdvectionRK4, - _keep_at_surface, - _check_error, - ], - endtime=actual_endtime, - dt=DT, - output_file=out_file, - verbose_progress=True, - ) diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 41185007..0d7294fb 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -1,137 +1,57 @@ -"""CTD instrument.""" - from dataclasses import dataclass -from datetime import timedelta from pathlib import Path -import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable +from virtualship.models import Spacetime, instruments -from virtualship.models import Spacetime +MYINSTRUMENT = "CTD" @dataclass class CTD: - """Configuration for a single CTD.""" + """CTD configuration.""" spacetime: Spacetime min_depth: float max_depth: float -_CTDParticle = JITParticle.add_variables( - [ - Variable("salinity", dtype=np.float32, initial=np.nan), - Variable("temperature", dtype=np.float32, initial=np.nan), - Variable("raising", dtype=np.int8, initial=0.0), # bool. 0 is False, 1 is True. - Variable("max_depth", dtype=np.float32), - Variable("min_depth", dtype=np.float32), - Variable("winch_speed", dtype=np.float32), - ] -) - - -def _sample_temperature(particle, fieldset, time): - particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] - - -def _sample_salinity(particle, fieldset, time): - particle.salinity = fieldset.S[time, particle.depth, particle.lat, particle.lon] - - -def _ctd_cast(particle, fieldset, time): - # lowering - if particle.raising == 0: - particle_ddepth = -particle.winch_speed * particle.dt - if particle.depth + particle_ddepth < particle.max_depth: - particle.raising = 1 - particle_ddepth = -particle_ddepth - # raising - else: - particle_ddepth = particle.winch_speed * particle.dt - if particle.depth + particle_ddepth > particle.min_depth: - particle.delete() - - -def simulate_ctd( - fieldset: FieldSet, - out_path: str | Path, - ctds: list[CTD], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of CTDs in a fieldset. - - :param fieldset: The fieldset to simulate the CTDs in. - :param out_path: The path to write the results to. - :param ctds: A list of CTDs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided CTDs, fieldset, are not compatible with this function. - """ - WINCH_SPEED = 1.0 # sink and rise speed in m/s - DT = 10.0 # dt of CTD simulation integrator - - if len(ctds) == 0: - print( - "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - - # deploy time for all ctds should be later than fieldset start time - if not all( - [np.datetime64(ctd.spacetime.time) >= fieldset_starttime for ctd in ctds] +# --------------- +# TODO: KERNELS +# --------------- + + +class CTDInputDataset(instruments.InputDataset): + """Input dataset for CTD instrument.""" + + def __init__(self): + """Initialise with instrument's name.""" + super().__init__(MYINSTRUMENT) + + def download_data(self, name: str) -> None: + """Download CTD data.""" + ... + + def get_dataset_path(self, name: str) -> Path: + """Get path to CTD dataset.""" + ... + + +class CTDInstrument(instruments.Instrument): + """CTD instrument class.""" + + def __init__( + self, + config, + input_dataset: CTDInputDataset, + kernels, ): - raise ValueError("CTD deployed before fieldset starts.") - - # depth the ctd will go to. shallowest between ctd max depth and bathymetry. - max_depths = [ - max( - ctd.max_depth, - fieldset.bathymetry.eval( - z=0, y=ctd.spacetime.location.lat, x=ctd.spacetime.location.lon, time=0 - ), - ) - for ctd in ctds - ] - - # CTD depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): - raise ValueError( - f"CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" - ) - - # define parcel particles - ctd_particleset = ParticleSet( - fieldset=fieldset, - pclass=_CTDParticle, - lon=[ctd.spacetime.location.lon for ctd in ctds], - lat=[ctd.spacetime.location.lat for ctd in ctds], - depth=[ctd.min_depth for ctd in ctds], - time=[ctd.spacetime.time for ctd in ctds], - max_depth=max_depths, - min_depth=[ctd.min_depth for ctd in ctds], - winch_speed=[WINCH_SPEED for _ in ctds], - ) - - # define output file for the simulation - out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt) - - # execute simulation - ctd_particleset.execute( - [_sample_salinity, _sample_temperature, _ctd_cast], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they resurface - if len(ctd_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." - ) + """Initialise with instrument's name.""" + super().__init__(MYINSTRUMENT, config, input_dataset, kernels) + + def load_fieldset(self): + """Load fieldset.""" + ... + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py deleted file mode 100644 index 5aef240f..00000000 --- a/src/virtualship/instruments/drifter.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Drifter instrument.""" - -from dataclasses import dataclass -from datetime import datetime, timedelta -from pathlib import Path - -import numpy as np -from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable - -from virtualship.models import Spacetime - - -@dataclass -class Drifter: - """Configuration for a single Drifter.""" - - spacetime: Spacetime - depth: float # depth at which it floats and samples - lifetime: timedelta | None # if none, lifetime is infinite - - -_DrifterParticle = JITParticle.add_variables( - [ - Variable("temperature", dtype=np.float32, initial=np.nan), - Variable("has_lifetime", dtype=np.int8), # bool - Variable("age", dtype=np.float32, initial=0.0), - Variable("lifetime", dtype=np.float32), - ] -) - - -def _sample_temperature(particle, fieldset, time): - particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] - - -def _check_lifetime(particle, fieldset, time): - if particle.has_lifetime == 1: - particle.age += particle.dt - if particle.age >= particle.lifetime: - particle.delete() - - -def simulate_drifters( - fieldset: FieldSet, - out_path: str | Path, - drifters: list[Drifter], - outputdt: timedelta, - dt: timedelta, - endtime: datetime | None = None, -) -> None: - """ - Use Parcels to simulate a set of drifters in a fieldset. - - :param fieldset: The fieldset to simulate the Drifters in. - :param out_path: The path to write the results to. - :param drifters: A list of drifters to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation. - :param dt: Dt for integration. - :param endtime: Stop at this time, or if None, continue until the end of the fieldset or until all drifters ended. If this is earlier than the last drifter ended or later than the end of the fieldset, a warning will be printed. - """ - if len(drifters) == 0: - print( - "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - # define parcel particles - drifter_particleset = ParticleSet( - fieldset=fieldset, - pclass=_DrifterParticle, - lat=[drifter.spacetime.location.lat for drifter in drifters], - lon=[drifter.spacetime.location.lon for drifter in drifters], - depth=[drifter.depth for drifter in drifters], - time=[drifter.spacetime.time for drifter in drifters], - has_lifetime=[1 if drifter.lifetime is not None else 0 for drifter in drifters], - lifetime=[ - 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() - for drifter in drifters - ], - ) - - # define output file for the simulation - out_file = drifter_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(drifter_particleset), 100] - ) - - # get earliest between fieldset end time and provide end time - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: - actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: - print("WARN: Requested end time later than fieldset end time.") - actual_endtime = fieldset_endtime - else: - actual_endtime = np.timedelta64(endtime) - - # execute simulation - drifter_particleset.execute( - [AdvectionRK4, _sample_temperature, _check_lifetime], - endtime=actual_endtime, - dt=dt, - output_file=out_file, - verbose_progress=True, - ) - - # if there are more particles left than the number of drifters with an indefinite endtime, warn the user - if len(drifter_particleset.particledata) > len( - [d for d in drifters if d.lifetime is None] - ): - print( - "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." - ) diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py deleted file mode 100644 index 7b08ad4b..00000000 --- a/src/virtualship/instruments/ship_underwater_st.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Ship salinity and temperature.""" - -from pathlib import Path - -import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable - -from virtualship.models import Spacetime - -# we specifically use ScipyParticle because we have many small calls to execute -# there is some overhead with JITParticle and this ends up being significantly faster -_ShipSTParticle = ScipyParticle.add_variables( - [ - Variable("S", dtype=np.float32, initial=np.nan), - Variable("T", dtype=np.float32, initial=np.nan), - ] -) - - -# define function sampling Salinity -def _sample_salinity(particle, fieldset, time): - particle.S = fieldset.S[time, particle.depth, particle.lat, particle.lon] - - -# define function sampling Temperature -def _sample_temperature(particle, fieldset, time): - particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] - - -def simulate_ship_underwater_st( - fieldset: FieldSet, - out_path: str | Path, - depth: float, - sample_points: list[Spacetime], -) -> None: - """ - Use Parcels to simulate underway data, measuring salinity and temperature at the given depth along the ship track in a fieldset. - - :param fieldset: The fieldset to simulate the sampling in. - :param out_path: The path to write the results to. - :param depth: The depth at which to measure. 0 is water surface, negative is into the water. - :param sample_points: The places and times to sample at. - """ - sample_points.sort(key=lambda p: p.time) - - particleset = ParticleSet.from_list( - fieldset=fieldset, - pclass=_ShipSTParticle, - lon=0.0, # initial lat/lon are irrelevant and will be overruled later - lat=0.0, - depth=depth, - time=0, # same for time - ) - - # define output file for the simulation - # outputdt set to infinie as we want to just want to write at the end of every call to 'execute' - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - - # iterate over each point, manually set lat lon time, then - # execute the particle set for one step, performing one set of measurement - for point in sample_points: - particleset.lon_nextloop[:] = point.location.lon - particleset.lat_nextloop[:] = point.location.lat - particleset.time_nextloop[:] = fieldset.time_origin.reltime( - np.datetime64(point.time) - ) - - # perform one step using the particleset - # dt and runtime are set so exactly one step is made. - particleset.execute( - [_sample_salinity, _sample_temperature], - dt=1, - runtime=1, - verbose_progress=False, - output_file=out_file, - ) diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py deleted file mode 100644 index 6d75be8c..00000000 --- a/src/virtualship/instruments/xbt.py +++ /dev/null @@ -1,141 +0,0 @@ -"""XBT instrument.""" - -from dataclasses import dataclass -from datetime import timedelta -from pathlib import Path - -import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable - -from virtualship.models import Spacetime - - -@dataclass -class XBT: - """Configuration for a single XBT.""" - - spacetime: Spacetime - min_depth: float - max_depth: float - fall_speed: float - deceleration_coefficient: float - - -_XBTParticle = JITParticle.add_variables( - [ - Variable("temperature", dtype=np.float32, initial=np.nan), - Variable("max_depth", dtype=np.float32), - Variable("min_depth", dtype=np.float32), - Variable("fall_speed", dtype=np.float32), - Variable("deceleration_coefficient", dtype=np.float32), - ] -) - - -def _sample_temperature(particle, fieldset, time): - particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] - - -def _xbt_cast(particle, fieldset, time): - particle_ddepth = -particle.fall_speed * particle.dt - - # update the fall speed from the quadractic fall-rate equation - # check https://doi.org/10.5194/os-7-231-2011 - particle.fall_speed = ( - particle.fall_speed - 2 * particle.deceleration_coefficient * particle.dt - ) - - # delete particle if depth is exactly max_depth - if particle.depth == particle.max_depth: - particle.delete() - - # set particle depth to max depth if it's too deep - if particle.depth + particle_ddepth < particle.max_depth: - particle_ddepth = particle.max_depth - particle.depth - - -def simulate_xbt( - fieldset: FieldSet, - out_path: str | Path, - xbts: list[XBT], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of XBTs in a fieldset. - - :param fieldset: The fieldset to simulate the XBTs in. - :param out_path: The path to write the results to. - :param xbts: A list of XBTs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided XBTs, fieldset, are not compatible with this function. - """ - DT = 10.0 # dt of XBT simulation integrator - - if len(xbts) == 0: - print( - "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - - # deploy time for all xbts should be later than fieldset start time - if not all( - [np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts] - ): - raise ValueError("XBT deployed before fieldset starts.") - - # depth the xbt will go to. shallowest between xbt max depth and bathymetry. - max_depths = [ - max( - xbt.max_depth, - fieldset.bathymetry.eval( - z=0, y=xbt.spacetime.location.lat, x=xbt.spacetime.location.lon, time=0 - ), - ) - for xbt in xbts - ] - - # initial fall speeds - initial_fall_speeds = [xbt.fall_speed for xbt in xbts] - - # XBT depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): - if not max_depth <= -DT * fall_speed: - raise ValueError( - f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}" - ) - - # define xbt particles - xbt_particleset = ParticleSet( - fieldset=fieldset, - pclass=_XBTParticle, - lon=[xbt.spacetime.location.lon for xbt in xbts], - lat=[xbt.spacetime.location.lat for xbt in xbts], - depth=[xbt.min_depth for xbt in xbts], - time=[xbt.spacetime.time for xbt in xbts], - max_depth=max_depths, - min_depth=[xbt.min_depth for xbt in xbts], - fall_speed=[xbt.fall_speed for xbt in xbts], - ) - - # define output file for the simulation - out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt) - - # execute simulation - xbt_particleset.execute( - [_sample_temperature, _xbt_cast], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they finish profiling - if len(xbt_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before XBT finished profiling. This most likely means the field time dimension did not match the simulation time span." - ) diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py new file mode 100644 index 00000000..8b5e701e --- /dev/null +++ b/src/virtualship/models/instruments.py @@ -0,0 +1,82 @@ +import abc +from collections.abc import Callable +from pathlib import Path + +from yaspin import yaspin + +from virtualship.utils import ( + ship_spinner, +) + +# TODO +# how much detail needs to be fed into InputDataset (i.e. how much it differs per instrument) +# may impact whether need a child class (e.g. CTDInputDataset) as well as just InputDataset +# or whether it could just be fed a `name` ... ? + +# ++ abc.abstractmethods could be useful for testing purposes...e.g. will fail if an instrumnet implementation doesn't adhere to the `Instrument` class standards + + +class InputDataset(abc.ABC): + """Base class for instrument input datasets.""" + + def __init__(self, name): + """Initialise input dataset.""" + self.name = name + + @abc.abstractmethod + def download_data(self, name: str) -> None: + """Download data for the instrument.""" + pass + + @abc.abstractmethod + def get_dataset_path(self, name: str) -> Path: + """Get path to the dataset.""" + pass + + +class Instrument(abc.ABC): + """Base class for instruments.""" + + def __init__( + self, + name: str, + config, + input_dataset: InputDataset, + kernels: list[Callable], + ): + """Initialise instrument.""" + self.name = name + self.config = config + self.input_dataset = input_dataset + self.kernels = kernels + + @abc.abstractmethod + def load_fieldset(self): + """Load fieldset for simulation.""" + pass + + def get_output_path(self, output_dir: Path) -> Path: + """Get output path for results.""" + return output_dir / f"{self.name}.zarr" + + def run(self): + """Run instrument simulation.""" + with yaspin( + text=f"Simulating {self.name} measurements... ", + side="right", + spinner=ship_spinner, + ) as spinner: + self.simulate() + spinner.ok("✅") + + @abc.abstractmethod + def simulate(self): + """Simulate instrument measurements.""" + pass + + +# e.g. pseudo-code ... +# TODO: (necessary?) how to dynamically assemble list of all instruments defined so that new instruments can be added only by changes in one place...? +available_instruments: list = ... +# for instrument in available_instruments: +# MyInstrument(instrument) From b4fbf2be6090173c8127bd668a6482b41978d496 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:07:41 +0100 Subject: [PATCH 02/56] ignore refactoring notes in gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4efdfe45..bd70d1d5 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,7 @@ src/virtualship/_version_setup.py .vscode/ .DS_Store + + +# Ignore temporary notes files for refactoring +_refactoring_notes/ From 2409517f0dd3302a3b8ef40b1514e6e497672cc4 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:08:44 +0100 Subject: [PATCH 03/56] add note to remove upon completing v1 dev --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bd70d1d5..d9903e71 100644 --- a/.gitignore +++ b/.gitignore @@ -181,4 +181,5 @@ src/virtualship/_version_setup.py # Ignore temporary notes files for refactoring +# TODO: remove when finished with v1 dev! _refactoring_notes/ From cfa7a0f840861ac96c65f31c3b7d41c13c994f79 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:58:59 +0100 Subject: [PATCH 04/56] scratch inputdataset objects integration to _fetch --- .gitignore | 5 - src/virtualship/cli/_fetch.py | 184 ++++----------- src/virtualship/instruments/ctd.py | 59 +++-- src/virtualship/instruments/ctd_bgc.py | 80 +++++++ src/virtualship/instruments/master.py | 66 ++++++ src/virtualship/models/instruments.py | 87 +++++-- src/virtualship/models/schedule.py | 236 +++++++++++++++++++ src/virtualship/models/ship_config.py | 310 +++++++++++++++++++++++++ 8 files changed, 850 insertions(+), 177 deletions(-) create mode 100644 src/virtualship/instruments/master.py create mode 100644 src/virtualship/models/schedule.py create mode 100644 src/virtualship/models/ship_config.py diff --git a/.gitignore b/.gitignore index d9903e71..4efdfe45 100644 --- a/.gitignore +++ b/.gitignore @@ -178,8 +178,3 @@ src/virtualship/_version_setup.py .vscode/ .DS_Store - - -# Ignore temporary notes files for refactoring -# TODO: remove when finished with v1 dev! -_refactoring_notes/ diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index bf35fe2f..127ee256 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -2,10 +2,12 @@ import hashlib import shutil -from datetime import datetime, timedelta +from datetime import datetime from pathlib import Path from typing import TYPE_CHECKING +import copernicusmarine +from copernicusmarine.core_functions.credentials_utils import InvalidUsernameOrPassword from pydantic import BaseModel from virtualship.errors import IncompleteDownloadError @@ -19,11 +21,10 @@ from virtualship.models import SpaceTimeRegion import click -import copernicusmarine -from copernicusmarine.core_functions.credentials_utils import InvalidUsernameOrPassword import virtualship.cli._creds as creds from virtualship.utils import EXPEDITION +from virtualship.instruments.master import INSTRUMENTS DOWNLOAD_METADATA = "download_metadata.yaml" @@ -38,15 +39,13 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None be provided on prompt, via command line arguments, or via a YAML config file. Run `virtualship fetch` on an expedition for more info. """ - from virtualship.models import InstrumentType - if sum([username is None, password is None]) == 1: raise ValueError("Both username and password must be provided when using CLI.") path = Path(path) - data_folder = path / "data" - data_folder.mkdir(exist_ok=True) + data_dir = path / "data" + data_dir.mkdir(exist_ok=True) expedition = _get_expedition(path) @@ -61,7 +60,8 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.space_time_region ) - existing_download = get_existing_download(data_folder, space_time_region_hash) + # TODO: this (below) probably needs updating! + existing_download = get_existing_download(data_dir, space_time_region_hash) if existing_download is not None: click.echo( f"Data download for space-time region already completed ('{existing_download}')." @@ -69,7 +69,10 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None return creds_path = path / creds.CREDENTIALS_FILE - username, password = creds.get_credentials_flow(username, password, creds_path) + credentials = {} + credentials["username"], credentials["password"] = creds.get_credentials_flow( + username, password, creds_path + ) # Extract space_time_region details from the schedule spatial_range = expedition.schedule.space_time_region.spatial_range @@ -79,13 +82,46 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None instruments_in_schedule = expedition.schedule.get_instruments() # Create download folder and set download metadata - download_folder = data_folder / hash_to_filename(space_time_region_hash) + download_folder = data_dir / hash_to_filename(space_time_region_hash) download_folder.mkdir() DownloadMetadata(download_complete=False).to_yaml( download_folder / DOWNLOAD_METADATA ) shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) + # bathymetry (required for all expeditions) + copernicusmarine.subset( + dataset_id="cmems_mod_glo_phy_my_0.083deg_static", + variables=["deptho"], + minimum_longitude=space_time_region.spatial_range.minimum_longitude, + maximum_longitude=space_time_region.spatial_range.maximum_longitude, + minimum_latitude=space_time_region.spatial_range.minimum_latitude, + maximum_latitude=space_time_region.spatial_range.maximum_latitude, + start_datetime=space_time_region.time_range.start_time, + end_datetime=space_time_region.time_range.start_time, + minimum_depth=abs(space_time_region.spatial_range.minimum_depth), + maximum_depth=abs(space_time_region.spatial_range.maximum_depth), + output_filename="bathymetry.nc", + output_directory=download_folder, + username=credentials["username"], + password=credentials["password"], + overwrite=True, + coordinates_selection_method="outside", + ) + + # keep only instruments in INTSTRUMENTS which are in schedule + filtered_instruments = { + k: v for k, v in INSTRUMENTS.items() if k in instruments_in_schedule + } + + # iterate across instruments and download data based on space_time_region + for _, instrument in filtered_instruments.items(): + try: + instrument["input_class"]( + data_dir=download_folder, + credentials=credentials, + space_time_region=space_time_region, + ) #! #### TODO # ++ new logic here where iterates (?) through available instruments and determines whether download is required: @@ -198,127 +234,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None shutil.rmtree(download_folder) raise e - click.echo("Drifter data download based on space-time region completed.") - - if InstrumentType.ARGO_FLOAT in instruments_in_schedule: - print("Argo float data will be downloaded. Please wait...") - argo_download_dict = { - "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", - "variables": ["uo", "vo"], - "output_filename": "argo_float_uv.nc", - }, - "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", - "variables": ["so"], - "output_filename": "argo_float_s.nc", - }, - "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", - "variables": ["thetao"], - "output_filename": "argo_float_t.nc", - }, - } - - # Iterate over all datasets and download each based on space_time_region - try: - for dataset in argo_download_dict.values(): - copernicusmarine.subset( - dataset_id=dataset["dataset_id"], - variables=dataset["variables"], - minimum_longitude=spatial_range.minimum_longitude - 3.0, - maximum_longitude=spatial_range.maximum_longitude + 3.0, - minimum_latitude=spatial_range.minimum_latitude - 3.0, - maximum_latitude=spatial_range.maximum_latitude + 3.0, - start_datetime=start_datetime, - end_datetime=end_datetime + timedelta(days=21), - minimum_depth=abs(1), - maximum_depth=abs(spatial_range.maximum_depth), - output_filename=dataset["output_filename"], - output_directory=download_folder, - username=username, - password=password, - overwrite=True, - coordinates_selection_method="outside", - ) - except InvalidUsernameOrPassword as e: - shutil.rmtree(download_folder) - raise e - - click.echo("Argo_float data download based on space-time region completed.") - - if InstrumentType.CTD_BGC in instruments_in_schedule: - print("CTD_BGC data will be downloaded. Please wait...") - - ctd_bgc_download_dict = { - "o2data": { - "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", - "variables": ["o2"], - "output_filename": "ctd_bgc_o2.nc", - }, - "chlorodata": { - "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "variables": ["chl"], - "output_filename": "ctd_bgc_chl.nc", - }, - "nitratedata": { - "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "variables": ["no3"], - "output_filename": "ctd_bgc_no3.nc", - }, - "phosphatedata": { - "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "variables": ["po4"], - "output_filename": "ctd_bgc_po4.nc", - }, - "phdata": { - "dataset_id": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", - "variables": ["ph"], - "output_filename": "ctd_bgc_ph.nc", - }, - "phytoplanktondata": { - "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "variables": ["phyc"], - "output_filename": "ctd_bgc_phyc.nc", - }, - "zooplanktondata": { - "dataset_id": "cmems_mod_glo_bgc-plankton_anfc_0.25deg_P1D-m", - "variables": ["zooc"], - "output_filename": "ctd_bgc_zooc.nc", - }, - "primaryproductiondata": { - "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", - "variables": ["nppv"], - "output_filename": "ctd_bgc_nppv.nc", - }, - } - - # Iterate over all datasets and download each based on space_time_region - try: - for dataset in ctd_bgc_download_dict.values(): - copernicusmarine.subset( - dataset_id=dataset["dataset_id"], - variables=dataset["variables"], - minimum_longitude=spatial_range.minimum_longitude - 3.0, - maximum_longitude=spatial_range.maximum_longitude + 3.0, - minimum_latitude=spatial_range.minimum_latitude - 3.0, - maximum_latitude=spatial_range.maximum_latitude + 3.0, - start_datetime=start_datetime, - end_datetime=end_datetime + timedelta(days=21), - minimum_depth=abs(1), - maximum_depth=abs(spatial_range.maximum_depth), - output_filename=dataset["output_filename"], - output_directory=download_folder, - username=username, - password=password, - overwrite=True, - coordinates_selection_method="outside", - ) - except InvalidUsernameOrPassword as e: - shutil.rmtree(download_folder) - raise e - - click.echo("CTD_BGC data download based on space-time region completed.") + click.echo(f"{instrument.name} data download completed.") # TODO complete_download(download_folder) @@ -386,11 +302,9 @@ def from_yaml(cls, file_path: str | Path) -> DownloadMetadata: return _generic_load_yaml(file_path, cls) -def get_existing_download( - data_folder: Path, space_time_region_hash: str -) -> Path | None: +def get_existing_download(data_dir: Path, space_time_region_hash: str) -> Path | None: """Check if a download has already been completed. If so, return the path for existing download.""" - for download_path in data_folder.rglob("*"): + for download_path in data_dir.rglob("*"): try: hash = filename_to_hash(download_path.name) except ValueError: diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 0d7294fb..624256cb 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -1,15 +1,17 @@ from dataclasses import dataclass -from pathlib import Path +from typing import ClassVar from virtualship.models import Spacetime, instruments -MYINSTRUMENT = "CTD" +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py @dataclass class CTD: """CTD configuration.""" + name: ClassVar[str] = "CTD" spacetime: Spacetime min_depth: float max_depth: float @@ -23,17 +25,36 @@ class CTD: class CTDInputDataset(instruments.InputDataset): """Input dataset for CTD instrument.""" - def __init__(self): - """Initialise with instrument's name.""" - super().__init__(MYINSTRUMENT) - - def download_data(self, name: str) -> None: - """Download CTD data.""" - ... + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 0.0, + "days": 0.0, + } # CTD data requires no buffers - def get_dataset_path(self, name: str) -> Path: - """Get path to CTD dataset.""" - ... + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + CTD.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "Sdata": { + "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "variables": ["so"], + "output_filename": f"{self.name}_s.nc", + }, + "Tdata": { + "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "variables": ["thetao"], + "output_filename": f"{self.name}_t.nc", + }, + } class CTDInstrument(instruments.Instrument): @@ -42,16 +63,18 @@ class CTDInstrument(instruments.Instrument): def __init__( self, config, - input_dataset: CTDInputDataset, + input_dataset, kernels, ): """Initialise with instrument's name.""" - super().__init__(MYINSTRUMENT, config, input_dataset, kernels) - - def load_fieldset(self): - """Load fieldset.""" - ... + super().__init__(CTD.name, config, input_dataset, kernels) def simulate(self): """Simulate measurements.""" ... + + +# # [PSEUDO-CODE] example implementation for reference +# ctd = CTDInstrument(config=CTD, data_dir=..., kernels=...) + +# ctd.simulate(...) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index fde92ca1..6b9b2f29 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -1,3 +1,83 @@ +# from dataclasses import dataclass +# from typing import ClassVar + +# from virtualship.models import Spacetime, instruments + +# MYINSTRUMENT = "CTD_BGC" + + +# @dataclass +# class CTD_BGC: +# """CTD_BGC configuration.""" + +# spacetime: Spacetime +# min_depth: float +# max_depth: float + + +# # --------------- +# # TODO: KERNELS +# # --------------- + + +# class CTD_BGCInputDataset(instruments.InputDataset): +# """Input dataset object for CTD_BGC instrument.""" + +# DOWNLOAD_BUFFERS: ClassVar[dict] = { +# "latlon_degrees": 0.0, +# "days": 0.0, +# } # CTD_BGC data requires no buffers + +# def __init__(self, data_dir, credentials, space_time_region): +# """Initialise with instrument's name.""" +# super().__init__( +# MYINSTRUMENT, +# self.DOWNLOAD_BUFFERS["latlon_degrees"], +# self.DOWNLOAD_BUFFERS["days"], +# data_dir, +# credentials, +# space_time_region, +# ) + +# def datasets_dir(self) -> dict: +# """Variable specific args for instrument.""" +# return { +# "o2data": { +# "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", +# "variables": ["o2"], +# "output_filename": "ctd_bgc_o2.nc", +# }, +# "chlorodata": { +# "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", +# "variables": ["chl"], +# "output_filename": "ctd_bgc_chloro.nc", +# }, +# } + + +# class CTD_BGCInstrument(instruments.Instrument): +# """CTD_BGC instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(MYINSTRUMENT, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +# # # [PSEUDO-CODE] example implementation for reference +# # ctd = CTD_BGCInstrument(config=CTD_BGC, data_dir=..., kernels=...) + +# # ctd.simulate(...) + + """CTD_BGC instrument.""" from dataclasses import dataclass diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py new file mode 100644 index 00000000..4705c8e3 --- /dev/null +++ b/src/virtualship/instruments/master.py @@ -0,0 +1,66 @@ +# + +# TODO: temporary measure so as not to have to overhaul the InstrumentType class logic in one go +#! And also to avoid breaking other parts of the codebase which rely on InstrumentType when for now just working on fetch +# TODO: ideally this can evaporate... +# TODO: discuss to see if there's a better option...! + +from enum import Enum + +# and so on ... +# from virtualship.instruments.ctd import CTDInputDataset, CTDInstrument + + +class InstrumentType(Enum): + """Types of the instruments.""" + + CTD = "CTD" + # CTD_BGC = "CTD_BGC" + # DRIFTER = "DRIFTER" + # ARGO_FLOAT = "ARGO_FLOAT" + # XBT = "XBT" + + # # TODO: should underway also be handled here?! + # ADCP = "ADCP" + # UNDERWAY_ST = "UNDERWAY_ST" + + +# replace with imports instead... +class CTDInputDataset: + """Input dataset class for CTD instrument.""" + + pass + + +class CTDInstrument: + """Instrument class for CTD instrument.""" + + pass + + +INSTRUMENTS = { + inst: { + "input_class": globals()[f"{inst.value}InputDataset"], + "instrument_class": globals()[f"{inst.value}Instrument"], + } + for inst in InstrumentType + if f"{inst.value}InputDataset" in globals() + and f"{inst.value}Instrument" in globals() +} + + +# INSTRUMENTS = { +# InstrumentType.CTD: { +# "input_class": CTDInputDataset, +# "instrument_class": CTDInstrument, +# } +# # and so on for other instruments... +# } + +# INSTRUMENTS = { +# "InstrumentType.CTD": { +# "input_class": "test", +# "instrument_class": "test", +# } +# # and so on for other instruments... +# } diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py index 8b5e701e..cddebd85 100644 --- a/src/virtualship/models/instruments.py +++ b/src/virtualship/models/instruments.py @@ -1,37 +1,86 @@ import abc from collections.abc import Callable +from datetime import timedelta from pathlib import Path +import copernicusmarine from yaspin import yaspin -from virtualship.utils import ( - ship_spinner, -) +from virtualship.models.space_time_region import SpaceTimeRegion +from virtualship.utils import ship_spinner -# TODO +# TODO list START # how much detail needs to be fed into InputDataset (i.e. how much it differs per instrument) # may impact whether need a child class (e.g. CTDInputDataset) as well as just InputDataset # or whether it could just be fed a `name` ... ? # ++ abc.abstractmethods could be useful for testing purposes...e.g. will fail if an instrumnet implementation doesn't adhere to the `Instrument` class standards +# ++ discussion point with others, do we think it's okay to overhaul the data downloading so that each instrument has it's own files, rather than sharing data? +# ++ it's a cleaner way of making the whole repo more modular, i.e. have higher order logic for defining data downloads and housing all instrument logic in one place... +# ++ may even not matter so much considering working towards cloud integration... + we are not looking to optimise performance...? +# ++ OR, for now work on it in this way and then at the end make some clever changes to consolidate to minimum number of files dependent on instrument selections...? +# TODO list END + class InputDataset(abc.ABC): """Base class for instrument input datasets.""" - def __init__(self, name): + def __init__( + self, + name: str, + latlon_buffer: float, + datetime_buffer: float, + data_dir: str, + credentials: dict, + space_time_region: SpaceTimeRegion, + ): """Initialise input dataset.""" self.name = name + self.latlon_buffer = latlon_buffer + self.datetime_buffer = datetime_buffer + self.data_dir = data_dir + self.credentials = credentials + self.space_time_region = space_time_region @abc.abstractmethod - def download_data(self, name: str) -> None: - """Download data for the instrument.""" - pass - - @abc.abstractmethod - def get_dataset_path(self, name: str) -> Path: - """Get path to the dataset.""" - pass + def get_datasets_dict(self) -> dict: + """Get parameters for instrument's variable(s) specific data download.""" + ... + + def download_data(self) -> None: + """Download data for the instrument using copernicusmarine.""" + parameter_args = dict( + minimum_longitude=self.space_time_region.spatial_range.minimum_longitude + - self.latlon_buffer, + maximum_longitude=self.space_time_region.spatial_range.maximum_longitude + + self.latlon_buffer, + minimum_latitude=self.space_time_region.spatial_range.minimum_latitude + - self.latlon_buffer, + maximum_latitude=self.space_time_region.spatial_range.maximum_latitude + + self.latlon_buffer, + start_datetime=self.space_time_region.time_range.start_time, + end_datetime=self.space_time_region.time_range.end_time + + timedelta(days=self.datetime_buffer), + minimum_depth=abs(self.space_time_region.spatial_range.minimum_depth), + maximum_depth=abs(self.space_time_region.spatial_range.maximum_depth), + output_directory=self.data_dir, + username=self.credentials["username"], + password=self.credentials["password"], + overwrite=True, + coordinates_selection_method="outside", + ) + + datasets_args = self.get_datasets_dict() + + for dataset in datasets_args.values(): + download_args = {**parameter_args, **dataset} + copernicusmarine.subset(**download_args) + + # def get_fieldset_paths(self) -> list: + # """List of paths for instrument's (downloaded) input data.""" + + # ... class Instrument(abc.ABC): @@ -47,13 +96,13 @@ def __init__( """Initialise instrument.""" self.name = name self.config = config - self.input_dataset = input_dataset + self.input_data = input_dataset self.kernels = kernels - @abc.abstractmethod - def load_fieldset(self): - """Load fieldset for simulation.""" - pass + # def load_fieldset(self): + # """Load fieldset for simulation.""" + # # paths = self.input_data.get_fieldset_paths() + # ... def get_output_path(self, output_dir: Path) -> Path: """Get output path for results.""" @@ -72,7 +121,7 @@ def run(self): @abc.abstractmethod def simulate(self): """Simulate instrument measurements.""" - pass + ... # e.g. pseudo-code ... diff --git a/src/virtualship/models/schedule.py b/src/virtualship/models/schedule.py new file mode 100644 index 00000000..091c23b4 --- /dev/null +++ b/src/virtualship/models/schedule.py @@ -0,0 +1,236 @@ +"""Schedule class.""" + +from __future__ import annotations + +import itertools +from datetime import datetime, timedelta +from pathlib import Path +from typing import TYPE_CHECKING + +import pydantic +import pyproj +import yaml + +from virtualship.errors import ScheduleError + +from .instruments import InstrumentType +from .location import Location +from .space_time_region import SpaceTimeRegion + +if TYPE_CHECKING: + from parcels import FieldSet + + from virtualship.expedition.input_data import InputData + +projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") + + +class Waypoint(pydantic.BaseModel): + """A Waypoint to sail to with an optional time and an optional instrument.""" + + location: Location + time: datetime | None = None + instrument: InstrumentType | list[InstrumentType] | None = None + + @pydantic.field_serializer("instrument") + def serialize_instrument(self, instrument): + """Ensure InstrumentType is serialized as a string (or list of strings).""" + if isinstance(instrument, list): + return [inst.value for inst in instrument] + return instrument.value if instrument else None + + +class Schedule(pydantic.BaseModel): + """Schedule of the virtual ship.""" + + waypoints: list[Waypoint] + space_time_region: SpaceTimeRegion | None = None + + model_config = pydantic.ConfigDict(extra="forbid") + + def to_yaml(self, file_path: str | Path) -> None: + """ + Write schedule to yaml file. + + :param file_path: Path to the file to write to. + """ + with open(file_path, "w") as file: + yaml.dump( + self.model_dump( + by_alias=True, + ), + file, + ) + + @classmethod + def from_yaml(cls, file_path: str | Path) -> Schedule: + """ + Load schedule from yaml file. + + :param file_path: Path to the file to load from. + :returns: The schedule. + """ + with open(file_path) as file: + data = yaml.safe_load(file) + return Schedule(**data) + + def get_instruments(self) -> set[InstrumentType]: + """ + Retrieve a set of unique instruments used in the schedule. + + This method iterates through all waypoints in the schedule and collects + the instruments associated with each waypoint. It returns a set of unique + instruments, either as objects or as names. + + :raises CheckpointError: If the past waypoints in the given schedule + have been changed compared to the checkpoint. + :return: set: A set of unique instruments used in the schedule. + + """ + instruments_in_schedule = [] + for waypoint in self.waypoints: + if waypoint.instrument: + for instrument in waypoint.instrument: + if instrument: + instruments_in_schedule.append(instrument) + return set(instruments_in_schedule) + + def verify( + self, + ship_speed: float, + input_data: InputData | None, + *, + check_space_time_region: bool = False, + ignore_missing_fieldsets: bool = False, + ) -> None: + """ + Verify the feasibility and correctness of the schedule's waypoints. + + This method checks various conditions to ensure the schedule is valid: + 1. At least one waypoint is provided. + 2. The first waypoint has a specified time. + 3. Waypoint times are in ascending order. + 4. All waypoints are in water (not on land). + 5. The ship can arrive on time at each waypoint given its speed. + + :param ship_speed: The ship's speed in knots. + :param input_data: An InputData object containing fieldsets used to check if waypoints are on water. + :param check_space_time_region: whether to check for missing space_time_region. + :param ignore_missing_fieldsets: whether to ignore warning for missing field sets. + :raises PlanningError: If any of the verification checks fail, indicating infeasible or incorrect waypoints. + :raises NotImplementedError: If an instrument in the schedule is not implemented. + :return: None. The method doesn't return a value but raises exceptions if verification fails. + """ + print("\nVerifying route... ") + + if check_space_time_region and self.space_time_region is None: + raise ScheduleError( + "space_time_region not found in schedule, please define it to fetch the data." + ) + + if len(self.waypoints) == 0: + raise ScheduleError("At least one waypoint must be provided.") + + # check first waypoint has a time + if self.waypoints[0].time is None: + raise ScheduleError("First waypoint must have a specified time.") + + # check waypoint times are in ascending order + timed_waypoints = [wp for wp in self.waypoints if wp.time is not None] + checks = [ + next.time >= cur.time for cur, next in itertools.pairwise(timed_waypoints) + ] + if not all(checks): + invalid_i = [i for i, c in enumerate(checks) if c] + raise ScheduleError( + f"Waypoint(s) {', '.join(f'#{i + 1}' for i in invalid_i)}: each waypoint should be timed after all previous waypoints", + ) + + # check if all waypoints are in water + # this is done by picking an arbitrary provided fieldset and checking if UV is not zero + + # get all available fieldsets + available_fieldsets = [] + if input_data is not None: + fieldsets = [ + input_data.adcp_fieldset, + input_data.argo_float_fieldset, + input_data.ctd_fieldset, + input_data.drifter_fieldset, + input_data.ship_underwater_st_fieldset, + ] + for fs in fieldsets: + if fs is not None: + available_fieldsets.append(fs) + + # check if there are any fieldsets, else it's an error + if len(available_fieldsets) == 0: + if not ignore_missing_fieldsets: + print( + "Cannot verify because no fieldsets have been loaded. This is probably " + "because you are not using any instruments in your schedule. This is not a problem, " + "but carefully check your waypoint locations manually." + ) + + else: + # pick any + fieldset = available_fieldsets[0] + # get waypoints with 0 UV + land_waypoints = [ + (wp_i, wp) + for wp_i, wp in enumerate(self.waypoints) + if _is_on_land_zero_uv(fieldset, wp) + ] + # raise an error if there are any + if len(land_waypoints) > 0: + raise ScheduleError( + f"The following waypoints are on land: {['#' + str(wp_i) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" + ) + + # check that ship will arrive on time at each waypoint (in case no unexpected event happen) + time = self.waypoints[0].time + for wp_i, (wp, wp_next) in enumerate( + zip(self.waypoints, self.waypoints[1:], strict=False) + ): + if wp.instrument is InstrumentType.CTD: + time += timedelta(minutes=20) + + geodinv: tuple[float, float, float] = projection.inv( + wp.location.lon, + wp.location.lat, + wp_next.location.lon, + wp_next.location.lat, + ) + distance = geodinv[2] + + time_to_reach = timedelta(seconds=distance / ship_speed * 3600 / 1852) + arrival_time = time + time_to_reach + + if wp_next.time is None: + time = arrival_time + elif arrival_time > wp_next.time: + raise ScheduleError( + f"Waypoint planning is not valid: would arrive too late at waypoint number {wp_i + 2}. " + f"location: {wp_next.location} time: {wp_next.time} instrument: {wp_next.instrument}" + ) + else: + time = wp_next.time + + print("... All good to go!") + + +def _is_on_land_zero_uv(fieldset: FieldSet, waypoint: Waypoint) -> bool: + """ + Check if waypoint is on land by assuming zero velocity means land. + + :param fieldset: The fieldset to sample the velocity from. + :param waypoint: The waypoint to check. + :returns: If the waypoint is on land. + """ + return fieldset.UV.eval( + 0, + fieldset.gridset.grids[0].depth[0], + waypoint.location.lat, + waypoint.location.lon, + applyConversion=False, + ) == (0.0, 0.0) diff --git a/src/virtualship/models/ship_config.py b/src/virtualship/models/ship_config.py new file mode 100644 index 00000000..61d3d390 --- /dev/null +++ b/src/virtualship/models/ship_config.py @@ -0,0 +1,310 @@ +"""ShipConfig and supporting classes.""" + +from __future__ import annotations + +from datetime import timedelta +from pathlib import Path +from typing import TYPE_CHECKING + +import pydantic +import yaml + +from virtualship.errors import ConfigError +from virtualship.models.instruments import InstrumentType +from virtualship.utils import _validate_numeric_mins_to_timedelta + +if TYPE_CHECKING: + from .schedule import Schedule + + +class ArgoFloatConfig(pydantic.BaseModel): + """Configuration for argos floats.""" + + min_depth_meter: float = pydantic.Field(le=0.0) + max_depth_meter: float = pydantic.Field(le=0.0) + drift_depth_meter: float = pydantic.Field(le=0.0) + vertical_speed_meter_per_second: float = pydantic.Field(lt=0.0) + cycle_days: float = pydantic.Field(gt=0.0) + drift_days: float = pydantic.Field(gt=0.0) + + +class ADCPConfig(pydantic.BaseModel): + """Configuration for ADCP instrument.""" + + max_depth_meter: float = pydantic.Field(le=0.0) + num_bins: int = pydantic.Field(gt=0.0) + period: timedelta = pydantic.Field( + serialization_alias="period_minutes", + validation_alias="period_minutes", + gt=timedelta(), + ) + + model_config = pydantic.ConfigDict(populate_by_name=True) + + @pydantic.field_serializer("period") + def _serialize_period(self, value: timedelta, _info): + return value.total_seconds() / 60.0 + + @pydantic.field_validator("period", mode="before") + def _validate_period(cls, value: int | float | timedelta) -> timedelta: + return _validate_numeric_mins_to_timedelta(value) + + +class CTDConfig(pydantic.BaseModel): + """Configuration for CTD instrument.""" + + stationkeeping_time: timedelta = pydantic.Field( + serialization_alias="stationkeeping_time_minutes", + validation_alias="stationkeeping_time_minutes", + gt=timedelta(), + ) + min_depth_meter: float = pydantic.Field(le=0.0) + max_depth_meter: float = pydantic.Field(le=0.0) + + model_config = pydantic.ConfigDict(populate_by_name=True) + + @pydantic.field_serializer("stationkeeping_time") + def _serialize_stationkeeping_time(self, value: timedelta, _info): + return value.total_seconds() / 60.0 + + @pydantic.field_validator("stationkeeping_time", mode="before") + def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta: + return _validate_numeric_mins_to_timedelta(value) + + +class CTD_BGCConfig(pydantic.BaseModel): + """Configuration for CTD_BGC instrument.""" + + stationkeeping_time: timedelta = pydantic.Field( + serialization_alias="stationkeeping_time_minutes", + validation_alias="stationkeeping_time_minutes", + gt=timedelta(), + ) + min_depth_meter: float = pydantic.Field(le=0.0) + max_depth_meter: float = pydantic.Field(le=0.0) + + model_config = pydantic.ConfigDict(populate_by_name=True) + + @pydantic.field_serializer("stationkeeping_time") + def _serialize_stationkeeping_time(self, value: timedelta, _info): + return value.total_seconds() / 60.0 + + @pydantic.field_validator("stationkeeping_time", mode="before") + def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta: + return _validate_numeric_mins_to_timedelta(value) + + +class ShipUnderwaterSTConfig(pydantic.BaseModel): + """Configuration for underwater ST.""" + + period: timedelta = pydantic.Field( + serialization_alias="period_minutes", + validation_alias="period_minutes", + gt=timedelta(), + ) + + model_config = pydantic.ConfigDict(populate_by_name=True) + + @pydantic.field_serializer("period") + def _serialize_period(self, value: timedelta, _info): + return value.total_seconds() / 60.0 + + @pydantic.field_validator("period", mode="before") + def _validate_period(cls, value: int | float | timedelta) -> timedelta: + return _validate_numeric_mins_to_timedelta(value) + + +class DrifterConfig(pydantic.BaseModel): + """Configuration for drifters.""" + + depth_meter: float = pydantic.Field(le=0.0) + lifetime: timedelta = pydantic.Field( + serialization_alias="lifetime_minutes", + validation_alias="lifetime_minutes", + gt=timedelta(), + ) + + model_config = pydantic.ConfigDict(populate_by_name=True) + + @pydantic.field_serializer("lifetime") + def _serialize_lifetime(self, value: timedelta, _info): + return value.total_seconds() / 60.0 + + @pydantic.field_validator("lifetime", mode="before") + def _validate_lifetime(cls, value: int | float | timedelta) -> timedelta: + return _validate_numeric_mins_to_timedelta(value) + + +class XBTConfig(pydantic.BaseModel): + """Configuration for xbt instrument.""" + + min_depth_meter: float = pydantic.Field(le=0.0) + max_depth_meter: float = pydantic.Field(le=0.0) + fall_speed_meter_per_second: float = pydantic.Field(gt=0.0) + deceleration_coefficient: float = pydantic.Field(gt=0.0) + + +class ShipConfig(pydantic.BaseModel): + """Configuration of the virtual ship.""" + + ship_speed_knots: float = pydantic.Field(gt=0.0) + """ + Velocity of the ship in knots. + """ + + argo_float_config: ArgoFloatConfig | None = None + """ + Argo float configuration. + + If None, no argo floats can be deployed. + """ + + adcp_config: ADCPConfig | None = None + """ + ADCP configuration. + + If None, no ADCP measurements will be performed. + """ + + ctd_config: CTDConfig | None = None + """ + CTD configuration. + + If None, no CTDs can be cast. + """ + + ctd_bgc_config: CTD_BGCConfig | None = None + """ + CTD_BGC configuration. + + If None, no BGC CTDs can be cast. + """ + + ship_underwater_st_config: ShipUnderwaterSTConfig | None = None + """ + Ship underwater salinity temperature measurementconfiguration. + + If None, no ST measurements will be performed. + """ + + drifter_config: DrifterConfig | None = None + """ + Drifter configuration. + + If None, no drifters can be deployed. + """ + + xbt_config: XBTConfig | None = None + """ + XBT configuration. + + If None, no XBTs can be cast. + """ + + model_config = pydantic.ConfigDict(extra="forbid") + + def to_yaml(self, file_path: str | Path) -> None: + """ + Write config to yaml file. + + :param file_path: Path to the file to write to. + """ + with open(file_path, "w") as file: + yaml.dump(self.model_dump(by_alias=True), file) + + @classmethod + def from_yaml(cls, file_path: str | Path) -> ShipConfig: + """ + Load config from yaml file. + + :param file_path: Path to the file to load from. + :returns: The config. + """ + with open(file_path) as file: + data = yaml.safe_load(file) + return ShipConfig(**data) + + def verify(self, schedule: Schedule) -> None: + """ + Verify the ship configuration against the provided schedule. + + This function performs two main tasks: + 1. Removes instrument configurations that are not present in the schedule. + 2. Verifies that all instruments in the schedule have corresponding configurations. + + Parameters + ---------- + schedule : Schedule + The schedule object containing the planned instruments and waypoints. + + Returns + ------- + None + + Raises + ------ + ConfigError + If an instrument in the schedule does not have a corresponding configuration. + + Notes + ----- + - Prints a message if a configuration is provided for an instrument not in the schedule. + - Sets the configuration to None for instruments not in the schedule. + - Raises a ConfigError for each instrument in the schedule that lacks a configuration. + + """ + instruments_in_schedule = schedule.get_instruments() + + for instrument in [ + "ARGO_FLOAT", + "DRIFTER", + "XBT", + "CTD", + "CTD_BGC", + ]: # TODO make instrument names consistent capitals or lowercase throughout codebase + if hasattr(self, instrument.lower() + "_config") and not any( + instrument == schedule_instrument.name + for schedule_instrument in instruments_in_schedule + ): + print(f"{instrument} configuration provided but not in schedule.") + setattr(self, instrument.lower() + "_config", None) + + # verify instruments in schedule have configuration + # TODO: the ConfigError message could be improved to explain that the **schedule** file has X instrument but the **ship_config** file does not + for instrument in instruments_in_schedule: + try: + InstrumentType(instrument) + except ValueError as e: + raise NotImplementedError("Instrument not supported.") from e + + if instrument == InstrumentType.ARGO_FLOAT and ( + not hasattr(self, "argo_float_config") or self.argo_float_config is None + ): + raise ConfigError( + "Planning has a waypoint with Argo float instrument, but configuration does not configure Argo floats." + ) + if instrument == InstrumentType.CTD and ( + not hasattr(self, "ctd_config") or self.ctd_config is None + ): + raise ConfigError( + "Planning has a waypoint with CTD instrument, but configuration does not configure CTDs." + ) + if instrument == InstrumentType.CTD_BGC and ( + not hasattr(self, "ctd_bgc_config") or self.ctd_bgc_config is None + ): + raise ConfigError( + "Planning has a waypoint with CTD_BGC instrument, but configuration does not configure CTD_BGCs." + ) + if instrument == InstrumentType.DRIFTER and ( + not hasattr(self, "drifter_config") or self.drifter_config is None + ): + raise ConfigError( + "Planning has a waypoint with drifter instrument, but configuration does not configure drifters." + ) + + if instrument == InstrumentType.XBT and ( + not hasattr(self, "xbt_config") or self.xbt_config is None + ): + raise ConfigError( + "Planning has a waypoint with XBT instrument, but configuration does not configure XBT." + ) From 851dd270c8cce9faeb8afdac6ebe0dfeda516a73 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:22:46 +0100 Subject: [PATCH 05/56] add call to download_data() --- src/virtualship/cli/_fetch.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 127ee256..094f8c8a 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -110,14 +110,14 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None ) # keep only instruments in INTSTRUMENTS which are in schedule - filtered_instruments = { + filter_instruments = { k: v for k, v in INSTRUMENTS.items() if k in instruments_in_schedule } # iterate across instruments and download data based on space_time_region - for _, instrument in filtered_instruments.items(): + for _, instrument in filter_instruments.items(): try: - instrument["input_class"]( + input_dataset = instrument["input_class"]( data_dir=download_folder, credentials=credentials, space_time_region=space_time_region, From a417c68b350fe055e76c14cbc93ed95424787571 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:32:52 +0200 Subject: [PATCH 06/56] Add new instrument classes and update InputDataset to include depth parameters --- src/virtualship/cli/_fetch.py | 5 +- src/virtualship/instruments/adcp.py | 70 ++++ src/virtualship/instruments/argo_float.py | 84 +++++ src/virtualship/instruments/ctd.py | 8 +- src/virtualship/instruments/ctd_bgc.py | 326 +++++------------- src/virtualship/instruments/drifter.py | 79 +++++ src/virtualship/instruments/master.py | 38 +- .../instruments/ship_underwater_st.py | 75 ++++ src/virtualship/instruments/xbt.py | 84 +++++ src/virtualship/models/instruments.py | 13 +- 10 files changed, 491 insertions(+), 291 deletions(-) create mode 100644 src/virtualship/instruments/adcp.py create mode 100644 src/virtualship/instruments/argo_float.py create mode 100644 src/virtualship/instruments/drifter.py create mode 100644 src/virtualship/instruments/ship_underwater_st.py create mode 100644 src/virtualship/instruments/xbt.py diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 094f8c8a..1979b153 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -89,7 +89,10 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None ) shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) - # bathymetry (required for all expeditions) + # bathymetry + # TODO: this logic means it is downloaded for all expeditions but is only needed for CTD, CTD_BGC and XBT... + # TODO: to discuss: fine to still download for all expeditions because small size and then less duplication + # TODO: or add as var in each of InputDataset objects per instrument because will be overwritten to disk anyway and therefore not duplicate? copernicusmarine.subset( dataset_id="cmems_mod_glo_phy_my_0.083deg_static", variables=["deptho"], diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py new file mode 100644 index 00000000..71d8e2af --- /dev/null +++ b/src/virtualship/instruments/adcp.py @@ -0,0 +1,70 @@ +from dataclasses import dataclass +from typing import ClassVar + +from virtualship.models import instruments + +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py + + +@dataclass +class ADCP: + """ADCP configuration.""" + + name: ClassVar[str] = "ADCP" + + +# --------------- +# TODO: KERNELS +# --------------- + + +class ADCPInputDataset(instruments.InputDataset): + """Input dataset for ADCP instrument.""" + + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 0.0, + "days": 0.0, + } # ADCP data requires no buffers + + DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} + + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + ADCP.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + space_time_region.spatial_range.minimum_depth, + space_time_region.spatial_range.maximum_depth, + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "UVdata": { + "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "variables": ["uo", "vo"], + "output_filename": f"{self.name}_uv.nc", + }, + } + + +class ADCPInstrument(instruments.Instrument): + """ADCP instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, + ): + """Initialise with instrument's name.""" + super().__init__(ADCP.name, config, input_dataset, kernels) + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py new file mode 100644 index 00000000..38eae990 --- /dev/null +++ b/src/virtualship/instruments/argo_float.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass +from datetime import timedelta +from typing import ClassVar + +from virtualship.models import Spacetime, instruments + +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py + + +@dataclass +class ArgoFloat: + """Argo float configuration.""" + + name: ClassVar[str] = "ArgoFloat" + spacetime: Spacetime + depth: float # depth at which it floats and samples + lifetime: timedelta | None # if none, lifetime is infinite + + +# --------------- +# TODO: KERNELS +# --------------- + + +class ArgoFloatInputDataset(instruments.InputDataset): + """Input dataset for ArgoFloat instrument.""" + + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 3.0, + "days": 21.0, + } + + DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} + + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + ArgoFloat.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + self.DOWNLOAD_LIMITS["min_depth"], + space_time_region.spatial_range.maximum_depth, + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "UVdata": { + "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "variables": ["uo", "vo"], + "output_filename": "argo_float_uv.nc", + }, + "Sdata": { + "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "variables": ["so"], + "output_filename": "argo_float_s.nc", + }, + "Tdata": { + "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "variables": ["thetao"], + "output_filename": "argo_float_t.nc", + }, + } + + +class ArgoFloatInstrument(instruments.Instrument): + """ArgoFloat instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, + ): + """Initialise with instrument's name.""" + super().__init__(ArgoFloat.name, config, input_dataset, kernels) + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 624256cb..09813241 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -36,6 +36,8 @@ def __init__(self, data_dir, credentials, space_time_region): CTD.name, self.DOWNLOAD_BUFFERS["latlon_degrees"], self.DOWNLOAD_BUFFERS["days"], + space_time_region.spatial_range.minimum_depth, + space_time_region.spatial_range.maximum_depth, data_dir, credentials, space_time_region, @@ -72,9 +74,3 @@ def __init__( def simulate(self): """Simulate measurements.""" ... - - -# # [PSEUDO-CODE] example implementation for reference -# ctd = CTDInstrument(config=CTD, data_dir=..., kernels=...) - -# ctd.simulate(...) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 6b9b2f29..77be7a06 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -1,263 +1,103 @@ -# from dataclasses import dataclass -# from typing import ClassVar - -# from virtualship.models import Spacetime, instruments - -# MYINSTRUMENT = "CTD_BGC" - - -# @dataclass -# class CTD_BGC: -# """CTD_BGC configuration.""" - -# spacetime: Spacetime -# min_depth: float -# max_depth: float - - -# # --------------- -# # TODO: KERNELS -# # --------------- - - -# class CTD_BGCInputDataset(instruments.InputDataset): -# """Input dataset object for CTD_BGC instrument.""" - -# DOWNLOAD_BUFFERS: ClassVar[dict] = { -# "latlon_degrees": 0.0, -# "days": 0.0, -# } # CTD_BGC data requires no buffers - -# def __init__(self, data_dir, credentials, space_time_region): -# """Initialise with instrument's name.""" -# super().__init__( -# MYINSTRUMENT, -# self.DOWNLOAD_BUFFERS["latlon_degrees"], -# self.DOWNLOAD_BUFFERS["days"], -# data_dir, -# credentials, -# space_time_region, -# ) - -# def datasets_dir(self) -> dict: -# """Variable specific args for instrument.""" -# return { -# "o2data": { -# "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", -# "variables": ["o2"], -# "output_filename": "ctd_bgc_o2.nc", -# }, -# "chlorodata": { -# "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", -# "variables": ["chl"], -# "output_filename": "ctd_bgc_chloro.nc", -# }, -# } - - -# class CTD_BGCInstrument(instruments.Instrument): -# """CTD_BGC instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(MYINSTRUMENT, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -# # # [PSEUDO-CODE] example implementation for reference -# # ctd = CTD_BGCInstrument(config=CTD_BGC, data_dir=..., kernels=...) - -# # ctd.simulate(...) - - -"""CTD_BGC instrument.""" - from dataclasses import dataclass -from datetime import timedelta -from pathlib import Path +from typing import ClassVar -import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable - -from virtualship.models import Spacetime +from virtualship.models import Spacetime, instruments @dataclass class CTD_BGC: - """Configuration for a single BGC CTD.""" + """CTD_BGC configuration.""" + name: ClassVar[str] = "CTD_BGC" spacetime: Spacetime min_depth: float max_depth: float -_CTD_BGCParticle = JITParticle.add_variables( - [ - Variable("o2", dtype=np.float32, initial=np.nan), - Variable("chl", dtype=np.float32, initial=np.nan), - Variable("no3", dtype=np.float32, initial=np.nan), - Variable("po4", dtype=np.float32, initial=np.nan), - Variable("ph", dtype=np.float32, initial=np.nan), - Variable("phyc", dtype=np.float32, initial=np.nan), - Variable("zooc", dtype=np.float32, initial=np.nan), - Variable("nppv", dtype=np.float32, initial=np.nan), - Variable("raising", dtype=np.int8, initial=0.0), # bool. 0 is False, 1 is True. - Variable("max_depth", dtype=np.float32), - Variable("min_depth", dtype=np.float32), - Variable("winch_speed", dtype=np.float32), - ] -) - - -def _sample_o2(particle, fieldset, time): - particle.o2 = fieldset.o2[time, particle.depth, particle.lat, particle.lon] +# --------------- +# TODO: KERNELS +# --------------- -def _sample_chlorophyll(particle, fieldset, time): - particle.chl = fieldset.chl[time, particle.depth, particle.lat, particle.lon] +class CTD_BGCInputDataset(instruments.InputDataset): + """Input dataset object for CTD_BGC instrument.""" + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 0.0, + "days": 0.0, + } # CTD_BGC data requires no buffers -def _sample_nitrate(particle, fieldset, time): - particle.no3 = fieldset.no3[time, particle.depth, particle.lat, particle.lon] - - -def _sample_phosphate(particle, fieldset, time): - particle.po4 = fieldset.po4[time, particle.depth, particle.lat, particle.lon] - - -def _sample_ph(particle, fieldset, time): - particle.ph = fieldset.ph[time, particle.depth, particle.lat, particle.lon] - - -def _sample_phytoplankton(particle, fieldset, time): - particle.phyc = fieldset.phyc[time, particle.depth, particle.lat, particle.lon] - - -def _sample_zooplankton(particle, fieldset, time): - particle.zooc = fieldset.zooc[time, particle.depth, particle.lat, particle.lon] - - -def _sample_primary_production(particle, fieldset, time): - particle.nppv = fieldset.nppv[time, particle.depth, particle.lat, particle.lon] - - -def _ctd_bgc_cast(particle, fieldset, time): - # lowering - if particle.raising == 0: - particle_ddepth = -particle.winch_speed * particle.dt - if particle.depth + particle_ddepth < particle.max_depth: - particle.raising = 1 - particle_ddepth = -particle_ddepth - # raising - else: - particle_ddepth = particle.winch_speed * particle.dt - if particle.depth + particle_ddepth > particle.min_depth: - particle.delete() - - -def simulate_ctd_bgc( - fieldset: FieldSet, - out_path: str | Path, - ctd_bgcs: list[CTD_BGC], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of BGC CTDs in a fieldset. - - :param fieldset: The fieldset to simulate the BGC CTDs in. - :param out_path: The path to write the results to. - :param ctds: A list of BGC CTDs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided BGC CTDs, fieldset, are not compatible with this function. - """ - WINCH_SPEED = 1.0 # sink and rise speed in m/s - DT = 10.0 # dt of CTD simulation integrator - - if len(ctd_bgcs) == 0: - print( - "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + CTD_BGC.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + space_time_region.spatial_range.minimum_depth, + space_time_region.spatial_range.maximum_depth, + data_dir, + credentials, + space_time_region, ) - # TODO when Parcels supports it this check can be removed. - return - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - - # deploy time for all ctds should be later than fieldset start time - if not all( - [ - np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime - for ctd_bgc in ctd_bgcs - ] + def datasets_dir(self) -> dict: + """Variable specific args for instrument.""" + return { + "o2data": { + "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "variables": ["o2"], + "output_filename": "ctd_bgc_o2.nc", + }, + "chlorodata": { + "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "variables": ["chl"], + "output_filename": "ctd_bgc_chl.nc", + }, + "nitratedata": { + "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "variables": ["no3"], + "output_filename": "ctd_bgc_no3.nc", + }, + "phosphatedata": { + "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "variables": ["po4"], + "output_filename": "ctd_bgc_po4.nc", + }, + "phdata": { + "dataset_id": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", + "variables": ["ph"], + "output_filename": "ctd_bgc_ph.nc", + }, + "phytoplanktondata": { + "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "variables": ["phyc"], + "output_filename": "ctd_bgc_phyc.nc", + }, + "zooplanktondata": { + "dataset_id": "cmems_mod_glo_bgc-plankton_anfc_0.25deg_P1D-m", + "variables": ["zooc"], + "output_filename": "ctd_bgc_zooc.nc", + }, + "primaryproductiondata": { + "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "variables": ["nppv"], + "output_filename": "ctd_bgc_nppv.nc", + }, + } + + +class CTD_BGCInstrument(instruments.Instrument): + """CTD_BGC instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, ): - raise ValueError("BGC CTD deployed before fieldset starts.") - - # depth the bgc ctd will go to. shallowest between bgc ctd max depth and bathymetry. - max_depths = [ - max( - ctd_bgc.max_depth, - fieldset.bathymetry.eval( - z=0, - y=ctd_bgc.spacetime.location.lat, - x=ctd_bgc.spacetime.location.lon, - time=0, - ), - ) - for ctd_bgc in ctd_bgcs - ] + """Initialise with instrument's name.""" + super().__init__(CTD_BGC.name, config, input_dataset, kernels) - # CTD depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): - raise ValueError( - f"BGC CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" - ) - - # define parcel particles - ctd_bgc_particleset = ParticleSet( - fieldset=fieldset, - pclass=_CTD_BGCParticle, - lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in ctd_bgcs], - lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in ctd_bgcs], - depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - time=[ctd_bgc.spacetime.time for ctd_bgc in ctd_bgcs], - max_depth=max_depths, - min_depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - winch_speed=[WINCH_SPEED for _ in ctd_bgcs], - ) - - # define output file for the simulation - out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt) - - # execute simulation - ctd_bgc_particleset.execute( - [ - _sample_o2, - _sample_chlorophyll, - _sample_nitrate, - _sample_phosphate, - _sample_ph, - _sample_phytoplankton, - _sample_zooplankton, - _sample_primary_production, - _ctd_bgc_cast, - ], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they resurface - if len(ctd_bgc_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before BGC CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." - ) + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py new file mode 100644 index 00000000..70ac4b7b --- /dev/null +++ b/src/virtualship/instruments/drifter.py @@ -0,0 +1,79 @@ +from dataclasses import dataclass +from datetime import timedelta +from typing import ClassVar + +from virtualship.models import Spacetime, instruments + +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py + + +@dataclass +class Drifter: + """Drifter configuration.""" + + name: ClassVar[str] = "Drifter" + spacetime: Spacetime + depth: float # depth at which it floats and samples + lifetime: timedelta | None # if none, lifetime is infinite + + +# --------------- +# TODO: KERNELS +# --------------- + + +class DrifterInputDataset(instruments.InputDataset): + """Input dataset for Drifter instrument.""" + + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 3.0, + "days": 21.0, + } + + DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1, "max_depth": 1} + + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + Drifter.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + self.DOWNLOAD_LIMITS["min_depth"], + self.DOWNLOAD_LIMITS["max_depth"], + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "UVdata": { + "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "variables": ["uo", "vo"], + "output_filename": "drifter_uv.nc", + }, + "Tdata": { + "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "variables": ["thetao"], + "output_filename": "drifter_t.nc", + }, + } + + +class DrifterInstrument(instruments.Instrument): + """Drifter instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, + ): + """Initialise with instrument's name.""" + super().__init__(Drifter.name, config, input_dataset, kernels) + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 4705c8e3..161348e2 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -15,29 +15,16 @@ class InstrumentType(Enum): """Types of the instruments.""" CTD = "CTD" - # CTD_BGC = "CTD_BGC" - # DRIFTER = "DRIFTER" - # ARGO_FLOAT = "ARGO_FLOAT" - # XBT = "XBT" + CTD_BGC = "CTD_BGC" + DRIFTER = "DRIFTER" + ARGO_FLOAT = "ARGO_FLOAT" + XBT = "XBT" # # TODO: should underway also be handled here?! # ADCP = "ADCP" # UNDERWAY_ST = "UNDERWAY_ST" -# replace with imports instead... -class CTDInputDataset: - """Input dataset class for CTD instrument.""" - - pass - - -class CTDInstrument: - """Instrument class for CTD instrument.""" - - pass - - INSTRUMENTS = { inst: { "input_class": globals()[f"{inst.value}InputDataset"], @@ -47,20 +34,3 @@ class CTDInstrument: if f"{inst.value}InputDataset" in globals() and f"{inst.value}Instrument" in globals() } - - -# INSTRUMENTS = { -# InstrumentType.CTD: { -# "input_class": CTDInputDataset, -# "instrument_class": CTDInstrument, -# } -# # and so on for other instruments... -# } - -# INSTRUMENTS = { -# "InstrumentType.CTD": { -# "input_class": "test", -# "instrument_class": "test", -# } -# # and so on for other instruments... -# } diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py new file mode 100644 index 00000000..f364b156 --- /dev/null +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -0,0 +1,75 @@ +from dataclasses import dataclass +from typing import ClassVar + +from virtualship.models import instruments + +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py + + +@dataclass +class Underwater_ST: + """Underwater_ST configuration.""" + + name: ClassVar[str] = "Underwater_ST" + + +# --------------- +# TODO: KERNELS +# --------------- + + +class Underwater_STInputDataset(instruments.InputDataset): + """Input dataset for Underwater_ST instrument.""" + + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 0.0, + "days": 0.0, + } # Underwater_ST data requires no buffers + + DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} + + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + Underwater_ST.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + -2.0, # is always at 2m depth + -2.0, # is always at 2m depth + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "Sdata": { + "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "variables": ["so"], + "output_filename": f"{self.name}_s.nc", + }, + "Tdata": { + "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "variables": ["thetao"], + "output_filename": f"{self.name}_t.nc", + }, + } + + +class Underwater_STInstrument(instruments.Instrument): + """Underwater_ST instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, + ): + """Initialise with instrument's name.""" + super().__init__(Underwater_ST.name, config, input_dataset, kernels) + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py new file mode 100644 index 00000000..dcec018b --- /dev/null +++ b/src/virtualship/instruments/xbt.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass +from datetime import timedelta +from typing import ClassVar + +from virtualship.models import Spacetime, instruments + +## TODO: __init__.py will also need updating! +# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py + + +@dataclass +class XBT: + """XBT configuration.""" + + name: ClassVar[str] = "XBT" + spacetime: Spacetime + depth: float # depth at which it floats and samples + lifetime: timedelta | None # if none, lifetime is infinite + + +# --------------- +# TODO: KERNELS +# --------------- + + +class XBTInputDataset(instruments.InputDataset): + """Input dataset for XBT instrument.""" + + DOWNLOAD_BUFFERS: ClassVar[dict] = { + "latlon_degrees": 3.0, + "days": 21.0, + } + + DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} + + def __init__(self, data_dir, credentials, space_time_region): + """Initialise with instrument's name.""" + super().__init__( + XBT.name, + self.DOWNLOAD_BUFFERS["latlon_degrees"], + self.DOWNLOAD_BUFFERS["days"], + self.DOWNLOAD_LIMITS["min_depth"], + space_time_region.spatial_range.maximum_depth, + data_dir, + credentials, + space_time_region, + ) + + def get_datasets_dict(self) -> dict: + """Get variable specific args for instrument.""" + return { + "UVdata": { + "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "variables": ["uo", "vo"], + "output_filename": "ship_uv.nc", + }, + "Sdata": { + "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "variables": ["so"], + "output_filename": "ship_s.nc", + }, + "Tdata": { + "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "variables": ["thetao"], + "output_filename": "ship_t.nc", + }, + } + + +class XBTInstrument(instruments.Instrument): + """XBT instrument class.""" + + def __init__( + self, + config, + input_dataset, + kernels, + ): + """Initialise with instrument's name.""" + super().__init__(XBT.name, config, input_dataset, kernels) + + def simulate(self): + """Simulate measurements.""" + ... diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py index cddebd85..57139add 100644 --- a/src/virtualship/models/instruments.py +++ b/src/virtualship/models/instruments.py @@ -31,6 +31,8 @@ def __init__( name: str, latlon_buffer: float, datetime_buffer: float, + min_depth: float, + max_depth: float, data_dir: str, credentials: dict, space_time_region: SpaceTimeRegion, @@ -39,6 +41,8 @@ def __init__( self.name = name self.latlon_buffer = latlon_buffer self.datetime_buffer = datetime_buffer + self.min_depth = min_depth + self.max_depth = max_depth self.data_dir = data_dir self.credentials = credentials self.space_time_region = space_time_region @@ -62,8 +66,8 @@ def download_data(self) -> None: start_datetime=self.space_time_region.time_range.start_time, end_datetime=self.space_time_region.time_range.end_time + timedelta(days=self.datetime_buffer), - minimum_depth=abs(self.space_time_region.spatial_range.minimum_depth), - maximum_depth=abs(self.space_time_region.spatial_range.maximum_depth), + minimum_depth=abs(self.min_depth), + maximum_depth=abs(self.max_depth), output_directory=self.data_dir, username=self.credentials["username"], password=self.credentials["password"], @@ -77,11 +81,6 @@ def download_data(self) -> None: download_args = {**parameter_args, **dataset} copernicusmarine.subset(**download_args) - # def get_fieldset_paths(self) -> list: - # """List of paths for instrument's (downloaded) input data.""" - - # ... - class Instrument(abc.ABC): """Base class for instruments.""" From a0c77abe91ae43f32889a39a1c26b0bfd0d178ca Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 7 Oct 2025 11:52:28 +0200 Subject: [PATCH 07/56] Refactor instrument handling in _fetch and update imports for consistency --- src/virtualship/cli/_fetch.py | 21 +++++++++- src/virtualship/instruments/master.py | 56 ++++++++++++++++----------- src/virtualship/models/instruments.py | 7 ---- src/virtualship/models/schedule.py | 2 +- src/virtualship/models/ship_config.py | 2 +- 5 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 1979b153..c258f044 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -25,9 +25,12 @@ import virtualship.cli._creds as creds from virtualship.utils import EXPEDITION from virtualship.instruments.master import INSTRUMENTS +from virtualship.instruments.master import InstrumentType, get_instruments_registry DOWNLOAD_METADATA = "download_metadata.yaml" +INSTRUMENTS = get_instruments_registry() + def _fetch(path: str | Path, username: str | None, password: str | None) -> None: """ @@ -81,6 +84,20 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None end_datetime = time_range.end_time instruments_in_schedule = expedition.schedule.get_instruments() + # TEMPORARY measure to get underway instruments in `instruments_in_schedule` + # TODO: should evaporate when schedule and ship_config.yaml files are consolidated in a separate PR... + if ship_config.adcp_config is not None: + instruments_in_schedule.add(InstrumentType.ADCP) + if ship_config.ship_underwater_st_config is not None: + instruments_in_schedule.add(InstrumentType.UNDERWATER_ST) + + # TEMPORARY measure to get underway instruments in `instruments_in_schedule` + # TODO: should evaporate when schedule and ship_config.yaml files are consolidated in a separate PR... + if ship_config.adcp_config is not None: + instruments_in_schedule.add(InstrumentType.ADCP) + if ship_config.ship_underwater_st_config is not None: + instruments_in_schedule.add(InstrumentType.UNDERWATER_ST) + # Create download folder and set download metadata download_folder = data_dir / hash_to_filename(space_time_region_hash) download_folder.mkdir() @@ -118,7 +135,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None } # iterate across instruments and download data based on space_time_region - for _, instrument in filter_instruments.items(): + for itype, instrument in filter_instruments.items(): try: input_dataset = instrument["input_class"]( data_dir=download_folder, @@ -237,7 +254,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None shutil.rmtree(download_folder) raise e - click.echo(f"{instrument.name} data download completed.") # TODO + click.echo(f"{itype.value} data download completed.") complete_download(download_folder) diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 161348e2..58eb0591 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -1,36 +1,46 @@ -# - -# TODO: temporary measure so as not to have to overhaul the InstrumentType class logic in one go -#! And also to avoid breaking other parts of the codebase which rely on InstrumentType when for now just working on fetch -# TODO: ideally this can evaporate... -# TODO: discuss to see if there's a better option...! - from enum import Enum -# and so on ... -# from virtualship.instruments.ctd import CTDInputDataset, CTDInstrument - class InstrumentType(Enum): """Types of the instruments.""" + # TODO: temporary measure so as not to have to overhaul the InstrumentType class logic in one go + #! And also to avoid breaking other parts of the codebase which rely on InstrumentType when for now just working on fetch + # TODO: ideally this can evaporate in a future PR... + CTD = "CTD" CTD_BGC = "CTD_BGC" DRIFTER = "DRIFTER" ARGO_FLOAT = "ARGO_FLOAT" XBT = "XBT" + ADCP = "ADCP" + UNDERWATER_ST = "UNDERWATER_ST" + + +def get_instruments_registry(): + # local imports to avoid circular import issues + from virtualship.instruments.adcp import ADCPInputDataset + from virtualship.instruments.argo_float import ArgoFloatInputDataset + from virtualship.instruments.ctd import CTDInputDataset + from virtualship.instruments.ctd_bgc import CTD_BGCInputDataset + from virtualship.instruments.drifter import DrifterInputDataset + from virtualship.instruments.ship_underwater_st import Underwater_STInputDataset + from virtualship.instruments.xbt import XBTInputDataset + + _input_class_map = { + "CTD": CTDInputDataset, + "CTD_BGC": CTD_BGCInputDataset, + "DRIFTER": DrifterInputDataset, + "ARGO_FLOAT": ArgoFloatInputDataset, + "XBT": XBTInputDataset, + "ADCP": ADCPInputDataset, + "UNDERWATER_ST": Underwater_STInputDataset, + } - # # TODO: should underway also be handled here?! - # ADCP = "ADCP" - # UNDERWAY_ST = "UNDERWAY_ST" - - -INSTRUMENTS = { - inst: { - "input_class": globals()[f"{inst.value}InputDataset"], - "instrument_class": globals()[f"{inst.value}Instrument"], + return { + inst: { + "input_class": _input_class_map.get(inst.value), + } + for inst in InstrumentType + if _input_class_map.get(inst.value) is not None } - for inst in InstrumentType - if f"{inst.value}InputDataset" in globals() - and f"{inst.value}Instrument" in globals() -} diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py index 57139add..7a356290 100644 --- a/src/virtualship/models/instruments.py +++ b/src/virtualship/models/instruments.py @@ -121,10 +121,3 @@ def run(self): def simulate(self): """Simulate instrument measurements.""" ... - - -# e.g. pseudo-code ... -# TODO: (necessary?) how to dynamically assemble list of all instruments defined so that new instruments can be added only by changes in one place...? -available_instruments: list = ... -# for instrument in available_instruments: -# MyInstrument(instrument) diff --git a/src/virtualship/models/schedule.py b/src/virtualship/models/schedule.py index 091c23b4..f3e5dabe 100644 --- a/src/virtualship/models/schedule.py +++ b/src/virtualship/models/schedule.py @@ -12,8 +12,8 @@ import yaml from virtualship.errors import ScheduleError +from virtualship.instruments.master import InstrumentType -from .instruments import InstrumentType from .location import Location from .space_time_region import SpaceTimeRegion diff --git a/src/virtualship/models/ship_config.py b/src/virtualship/models/ship_config.py index 61d3d390..ba7d221f 100644 --- a/src/virtualship/models/ship_config.py +++ b/src/virtualship/models/ship_config.py @@ -10,7 +10,7 @@ import yaml from virtualship.errors import ConfigError -from virtualship.models.instruments import InstrumentType +from virtualship.instruments.master import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta if TYPE_CHECKING: From 9bcaba2864566b02b5f8ec4ceeba463b2152aba0 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 7 Oct 2025 11:53:34 +0200 Subject: [PATCH 08/56] Refactor instrument classes and re-add (temporary) simulation functions across multiple files --- src/virtualship/instruments/adcp.py | 111 ++++++++-- src/virtualship/instruments/argo_float.py | 200 ++++++++++++++++-- src/virtualship/instruments/ctd.py | 148 +++++++++++-- src/virtualship/instruments/ctd_bgc.py | 196 +++++++++++++++-- src/virtualship/instruments/drifter.py | 129 +++++++++-- .../instruments/ship_underwater_st.py | 102 +++++++-- src/virtualship/instruments/xbt.py | 151 +++++++++++-- 7 files changed, 917 insertions(+), 120 deletions(-) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 71d8e2af..052004fb 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -1,7 +1,11 @@ from dataclasses import dataclass +from pathlib import Path from typing import ClassVar -from virtualship.models import instruments +import numpy as np +from parcels import FieldSet, ParticleSet, ScipyParticle, Variable + +from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! # + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py @@ -14,9 +18,20 @@ class ADCP: name: ClassVar[str] = "ADCP" -# --------------- -# TODO: KERNELS -# --------------- +# we specifically use ScipyParticle because we have many small calls to execute +# there is some overhead with JITParticle and this ends up being significantly faster +_ADCPParticle = ScipyParticle.add_variables( + [ + Variable("U", dtype=np.float32, initial=np.nan), + Variable("V", dtype=np.float32, initial=np.nan), + ] +) + + +def _sample_velocity(particle, fieldset, time): + particle.U, particle.V = fieldset.UV.eval( + time, particle.depth, particle.lat, particle.lon, applyConversion=False + ) class ADCPInputDataset(instruments.InputDataset): @@ -53,18 +68,78 @@ def get_datasets_dict(self) -> dict: } -class ADCPInstrument(instruments.Instrument): - """ADCP instrument class.""" - - def __init__( - self, - config, - input_dataset, - kernels, - ): - """Initialise with instrument's name.""" - super().__init__(ADCP.name, config, input_dataset, kernels) +# TODO: uncomment when ready for new simulation logic! +# class ADCPInstrument(instruments.Instrument): +# """ADCP instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(ADCP.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +# TODO: to be replaced with new simulation logic +## -- old simulation code + + +def simulate_adcp( + fieldset: FieldSet, + out_path: str | Path, + max_depth: float, + min_depth: float, + num_bins: int, + sample_points: list[Spacetime], +) -> None: + """ + Use Parcels to simulate an ADCP in a fieldset. + + :param fieldset: The fieldset to simulate the ADCP in. + :param out_path: The path to write the results to. + :param max_depth: Maximum depth the ADCP can measure. + :param min_depth: Minimum depth the ADCP can measure. + :param num_bins: How many samples to take in the complete range between max_depth and min_depth. + :param sample_points: The places and times to sample at. + """ + sample_points.sort(key=lambda p: p.time) + + bins = np.linspace(max_depth, min_depth, num_bins) + num_particles = len(bins) + particleset = ParticleSet.from_list( + fieldset=fieldset, + pclass=_ADCPParticle, + lon=np.full( + num_particles, 0.0 + ), # initial lat/lon are irrelevant and will be overruled later. + lat=np.full(num_particles, 0.0), + depth=bins, + time=0, # same for time + ) + + # define output file for the simulation + # outputdt set to infinite as we just want to write at the end of every call to 'execute' + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + + for point in sample_points: + particleset.lon_nextloop[:] = point.location.lon + particleset.lat_nextloop[:] = point.location.lat + particleset.time_nextloop[:] = fieldset.time_origin.reltime( + np.datetime64(point.time) + ) - def simulate(self): - """Simulate measurements.""" - ... + # perform one step using the particleset + # dt and runtime are set so exactly one step is made. + particleset.execute( + [_sample_velocity], + dt=1, + runtime=1, + verbose_progress=False, + output_file=out_file, + ) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 38eae990..4769eccf 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -1,7 +1,19 @@ +import math from dataclasses import dataclass -from datetime import timedelta +from datetime import datetime, timedelta +from pathlib import Path from typing import ClassVar +import numpy as np +from parcels import ( + AdvectionRK4, + FieldSet, + JITParticle, + ParticleSet, + StatusCode, + Variable, +) + from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! @@ -18,9 +30,88 @@ class ArgoFloat: lifetime: timedelta | None # if none, lifetime is infinite -# --------------- -# TODO: KERNELS -# --------------- +_ArgoParticle = JITParticle.add_variables( + [ + Variable("cycle_phase", dtype=np.int32, initial=0.0), + Variable("cycle_age", dtype=np.float32, initial=0.0), + Variable("drift_age", dtype=np.float32, initial=0.0), + Variable("salinity", dtype=np.float32, initial=np.nan), + Variable("temperature", dtype=np.float32, initial=np.nan), + Variable("min_depth", dtype=np.float32), + Variable("max_depth", dtype=np.float32), + Variable("drift_depth", dtype=np.float32), + Variable("vertical_speed", dtype=np.float32), + Variable("cycle_days", dtype=np.int32), + Variable("drift_days", dtype=np.int32), + ] +) + + +def _argo_float_vertical_movement(particle, fieldset, time): + if particle.cycle_phase == 0: + # Phase 0: Sinking with vertical_speed until depth is drift_depth + particle_ddepth += ( # noqa Parcels defines particle_* variables, which code checkers cannot know. + particle.vertical_speed * particle.dt + ) + if particle.depth + particle_ddepth <= particle.drift_depth: + particle_ddepth = particle.drift_depth - particle.depth + particle.cycle_phase = 1 + + elif particle.cycle_phase == 1: + # Phase 1: Drifting at depth for drifttime seconds + particle.drift_age += particle.dt + if particle.drift_age >= particle.drift_days * 86400: + particle.drift_age = 0 # reset drift_age for next cycle + particle.cycle_phase = 2 + + elif particle.cycle_phase == 2: + # Phase 2: Sinking further to max_depth + particle_ddepth += particle.vertical_speed * particle.dt + if particle.depth + particle_ddepth <= particle.max_depth: + particle_ddepth = particle.max_depth - particle.depth + particle.cycle_phase = 3 + + elif particle.cycle_phase == 3: + # Phase 3: Rising with vertical_speed until at surface + particle_ddepth -= particle.vertical_speed * particle.dt + particle.cycle_age += ( + particle.dt + ) # solve issue of not updating cycle_age during ascent + if particle.depth + particle_ddepth >= particle.min_depth: + particle_ddepth = particle.min_depth - particle.depth + particle.temperature = ( + math.nan + ) # reset temperature to NaN at end of sampling cycle + particle.salinity = math.nan # idem + particle.cycle_phase = 4 + else: + particle.temperature = fieldset.T[ + time, particle.depth, particle.lat, particle.lon + ] + particle.salinity = fieldset.S[ + time, particle.depth, particle.lat, particle.lon + ] + + elif particle.cycle_phase == 4: + # Phase 4: Transmitting at surface until cycletime is reached + if particle.cycle_age > particle.cycle_days * 86400: + particle.cycle_phase = 0 + particle.cycle_age = 0 + + if particle.state == StatusCode.Evaluate: + particle.cycle_age += particle.dt # update cycle_age + + +def _keep_at_surface(particle, fieldset, time): + # Prevent error when float reaches surface + if particle.state == StatusCode.ErrorThroughSurface: + particle.depth = particle.min_depth + particle.state = StatusCode.Success + + +def _check_error(particle, fieldset, time): + if particle.state >= 50: # This captures all Errors + particle.delete() class ArgoFloatInputDataset(instruments.InputDataset): @@ -67,18 +158,89 @@ def get_datasets_dict(self) -> dict: } -class ArgoFloatInstrument(instruments.Instrument): - """ArgoFloat instrument class.""" - - def __init__( - self, - config, - input_dataset, - kernels, - ): - """Initialise with instrument's name.""" - super().__init__(ArgoFloat.name, config, input_dataset, kernels) - - def simulate(self): - """Simulate measurements.""" - ... +# class ArgoFloatInstrument(instruments.Instrument): +# """ArgoFloat instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(ArgoFloat.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_argo_floats( + fieldset: FieldSet, + out_path: str | Path, + argo_floats: list[ArgoFloat], + outputdt: timedelta, + endtime: datetime | None, +) -> None: + """ + Use Parcels to simulate a set of Argo floats in a fieldset. + + :param fieldset: The fieldset to simulate the Argo floats in. + :param out_path: The path to write the results to. + :param argo_floats: A list of Argo floats to simulate. + :param outputdt: Interval which dictates the update frequency of file output during simulation + :param endtime: Stop at this time, or if None, continue until the end of the fieldset. + """ + DT = 10.0 # dt of Argo float simulation integrator + + if len(argo_floats) == 0: + print( + "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + # define parcel particles + argo_float_particleset = ParticleSet( + fieldset=fieldset, + pclass=_ArgoParticle, + lat=[argo.spacetime.location.lat for argo in argo_floats], + lon=[argo.spacetime.location.lon for argo in argo_floats], + depth=[argo.min_depth for argo in argo_floats], + time=[argo.spacetime.time for argo in argo_floats], + min_depth=[argo.min_depth for argo in argo_floats], + max_depth=[argo.max_depth for argo in argo_floats], + drift_depth=[argo.drift_depth for argo in argo_floats], + vertical_speed=[argo.vertical_speed for argo in argo_floats], + cycle_days=[argo.cycle_days for argo in argo_floats], + drift_days=[argo.drift_days for argo in argo_floats], + ) + + # define output file for the simulation + out_file = argo_float_particleset.ParticleFile( + name=out_path, outputdt=outputdt, chunks=[len(argo_float_particleset), 100] + ) + + # get earliest between fieldset end time and provide end time + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + if endtime is None: + actual_endtime = fieldset_endtime + elif endtime > fieldset_endtime: + print("WARN: Requested end time later than fieldset end time.") + actual_endtime = fieldset_endtime + else: + actual_endtime = np.timedelta64(endtime) + + # execute simulation + argo_float_particleset.execute( + [ + _argo_float_vertical_movement, + AdvectionRK4, + _keep_at_surface, + _check_error, + ], + endtime=actual_endtime, + dt=DT, + output_file=out_file, + verbose_progress=True, + ) diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 09813241..070cdb78 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -1,6 +1,11 @@ from dataclasses import dataclass +from datetime import timedelta +from pathlib import Path from typing import ClassVar +import numpy as np +from parcels import FieldSet, JITParticle, ParticleSet, Variable + from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! @@ -17,9 +22,38 @@ class CTD: max_depth: float -# --------------- -# TODO: KERNELS -# --------------- +_CTDParticle = JITParticle.add_variables( + [ + Variable("salinity", dtype=np.float32, initial=np.nan), + Variable("temperature", dtype=np.float32, initial=np.nan), + Variable("raising", dtype=np.int8, initial=0.0), # bool. 0 is False, 1 is True. + Variable("max_depth", dtype=np.float32), + Variable("min_depth", dtype=np.float32), + Variable("winch_speed", dtype=np.float32), + ] +) + + +def _sample_temperature(particle, fieldset, time): + particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] + + +def _sample_salinity(particle, fieldset, time): + particle.salinity = fieldset.S[time, particle.depth, particle.lat, particle.lon] + + +def _ctd_cast(particle, fieldset, time): + # lowering + if particle.raising == 0: + particle_ddepth = -particle.winch_speed * particle.dt + if particle.depth + particle_ddepth < particle.max_depth: + particle.raising = 1 + particle_ddepth = -particle_ddepth + # raising + else: + particle_ddepth = particle.winch_speed * particle.dt + if particle.depth + particle_ddepth > particle.min_depth: + particle.delete() class CTDInputDataset(instruments.InputDataset): @@ -59,18 +93,102 @@ def get_datasets_dict(self) -> dict: } -class CTDInstrument(instruments.Instrument): - """CTD instrument class.""" +# class CTDInstrument(instruments.Instrument): +# """CTD instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(CTD.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_ctd( + fieldset: FieldSet, + out_path: str | Path, + ctds: list[CTD], + outputdt: timedelta, +) -> None: + """ + Use Parcels to simulate a set of CTDs in a fieldset. + + :param fieldset: The fieldset to simulate the CTDs in. + :param out_path: The path to write the results to. + :param ctds: A list of CTDs to simulate. + :param outputdt: Interval which dictates the update frequency of file output during simulation + :raises ValueError: Whenever provided CTDs, fieldset, are not compatible with this function. + """ + WINCH_SPEED = 1.0 # sink and rise speed in m/s + DT = 10.0 # dt of CTD simulation integrator + + if len(ctds) == 0: + print( + "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - def __init__( - self, - config, - input_dataset, - kernels, + # deploy time for all ctds should be later than fieldset start time + if not all( + [np.datetime64(ctd.spacetime.time) >= fieldset_starttime for ctd in ctds] ): - """Initialise with instrument's name.""" - super().__init__(CTD.name, config, input_dataset, kernels) + raise ValueError("CTD deployed before fieldset starts.") + + # depth the ctd will go to. shallowest between ctd max depth and bathymetry. + max_depths = [ + max( + ctd.max_depth, + fieldset.bathymetry.eval( + z=0, y=ctd.spacetime.location.lat, x=ctd.spacetime.location.lon, time=0 + ), + ) + for ctd in ctds + ] + + # CTD depth can not be too shallow, because kernel would break. + # This shallow is not useful anyway, no need to support. + if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): + raise ValueError( + f"CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + ) - def simulate(self): - """Simulate measurements.""" - ... + # define parcel particles + ctd_particleset = ParticleSet( + fieldset=fieldset, + pclass=_CTDParticle, + lon=[ctd.spacetime.location.lon for ctd in ctds], + lat=[ctd.spacetime.location.lat for ctd in ctds], + depth=[ctd.min_depth for ctd in ctds], + time=[ctd.spacetime.time for ctd in ctds], + max_depth=max_depths, + min_depth=[ctd.min_depth for ctd in ctds], + winch_speed=[WINCH_SPEED for _ in ctds], + ) + + # define output file for the simulation + out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt) + + # execute simulation + ctd_particleset.execute( + [_sample_salinity, _sample_temperature, _ctd_cast], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, + ) + + # there should be no particles left, as they delete themselves when they resurface + if len(ctd_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + ) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 77be7a06..1025a5c8 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -1,6 +1,11 @@ from dataclasses import dataclass +from datetime import timedelta +from pathlib import Path from typing import ClassVar +import numpy as np +from parcels import FieldSet, JITParticle, ParticleSet, Variable + from virtualship.models import Spacetime, instruments @@ -14,9 +19,68 @@ class CTD_BGC: max_depth: float -# --------------- -# TODO: KERNELS -# --------------- +_CTD_BGCParticle = JITParticle.add_variables( + [ + Variable("o2", dtype=np.float32, initial=np.nan), + Variable("chl", dtype=np.float32, initial=np.nan), + Variable("no3", dtype=np.float32, initial=np.nan), + Variable("po4", dtype=np.float32, initial=np.nan), + Variable("ph", dtype=np.float32, initial=np.nan), + Variable("phyc", dtype=np.float32, initial=np.nan), + Variable("zooc", dtype=np.float32, initial=np.nan), + Variable("nppv", dtype=np.float32, initial=np.nan), + Variable("raising", dtype=np.int8, initial=0.0), # bool. 0 is False, 1 is True. + Variable("max_depth", dtype=np.float32), + Variable("min_depth", dtype=np.float32), + Variable("winch_speed", dtype=np.float32), + ] +) + + +def _sample_o2(particle, fieldset, time): + particle.o2 = fieldset.o2[time, particle.depth, particle.lat, particle.lon] + + +def _sample_chlorophyll(particle, fieldset, time): + particle.chl = fieldset.chl[time, particle.depth, particle.lat, particle.lon] + + +def _sample_nitrate(particle, fieldset, time): + particle.no3 = fieldset.no3[time, particle.depth, particle.lat, particle.lon] + + +def _sample_phosphate(particle, fieldset, time): + particle.po4 = fieldset.po4[time, particle.depth, particle.lat, particle.lon] + + +def _sample_ph(particle, fieldset, time): + particle.ph = fieldset.ph[time, particle.depth, particle.lat, particle.lon] + + +def _sample_phytoplankton(particle, fieldset, time): + particle.phyc = fieldset.phyc[time, particle.depth, particle.lat, particle.lon] + + +def _sample_zooplankton(particle, fieldset, time): + particle.zooc = fieldset.zooc[time, particle.depth, particle.lat, particle.lon] + + +def _sample_primary_production(particle, fieldset, time): + particle.nppv = fieldset.nppv[time, particle.depth, particle.lat, particle.lon] + + +def _ctd_bgc_cast(particle, fieldset, time): + # lowering + if particle.raising == 0: + particle_ddepth = -particle.winch_speed * particle.dt + if particle.depth + particle_ddepth < particle.max_depth: + particle.raising = 1 + particle_ddepth = -particle_ddepth + # raising + else: + particle_ddepth = particle.winch_speed * particle.dt + if particle.depth + particle_ddepth > particle.min_depth: + particle.delete() class CTD_BGCInputDataset(instruments.InputDataset): @@ -40,7 +104,7 @@ def __init__(self, data_dir, credentials, space_time_region): space_time_region, ) - def datasets_dir(self) -> dict: + def get_datasets_dict(self) -> dict: """Variable specific args for instrument.""" return { "o2data": { @@ -86,18 +150,118 @@ def datasets_dir(self) -> dict: } -class CTD_BGCInstrument(instruments.Instrument): - """CTD_BGC instrument class.""" +# class CTD_BGCInstrument(instruments.Instrument): +# """CTD_BGC instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(CTD_BGC.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_ctd_bgc( + fieldset: FieldSet, + out_path: str | Path, + ctd_bgcs: list[CTD_BGC], + outputdt: timedelta, +) -> None: + """ + Use Parcels to simulate a set of BGC CTDs in a fieldset. + + :param fieldset: The fieldset to simulate the BGC CTDs in. + :param out_path: The path to write the results to. + :param ctds: A list of BGC CTDs to simulate. + :param outputdt: Interval which dictates the update frequency of file output during simulation + :raises ValueError: Whenever provided BGC CTDs, fieldset, are not compatible with this function. + """ + WINCH_SPEED = 1.0 # sink and rise speed in m/s + DT = 10.0 # dt of CTD simulation integrator + + if len(ctd_bgcs) == 0: + print( + "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - def __init__( - self, - config, - input_dataset, - kernels, + # deploy time for all ctds should be later than fieldset start time + if not all( + [ + np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime + for ctd_bgc in ctd_bgcs + ] ): - """Initialise with instrument's name.""" - super().__init__(CTD_BGC.name, config, input_dataset, kernels) + raise ValueError("BGC CTD deployed before fieldset starts.") + + # depth the bgc ctd will go to. shallowest between bgc ctd max depth and bathymetry. + max_depths = [ + max( + ctd_bgc.max_depth, + fieldset.bathymetry.eval( + z=0, + y=ctd_bgc.spacetime.location.lat, + x=ctd_bgc.spacetime.location.lon, + time=0, + ), + ) + for ctd_bgc in ctd_bgcs + ] + + # CTD depth can not be too shallow, because kernel would break. + # This shallow is not useful anyway, no need to support. + if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): + raise ValueError( + f"BGC CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + ) - def simulate(self): - """Simulate measurements.""" - ... + # define parcel particles + ctd_bgc_particleset = ParticleSet( + fieldset=fieldset, + pclass=_CTD_BGCParticle, + lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in ctd_bgcs], + lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in ctd_bgcs], + depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], + time=[ctd_bgc.spacetime.time for ctd_bgc in ctd_bgcs], + max_depth=max_depths, + min_depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], + winch_speed=[WINCH_SPEED for _ in ctd_bgcs], + ) + + # define output file for the simulation + out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt) + + # execute simulation + ctd_bgc_particleset.execute( + [ + _sample_o2, + _sample_chlorophyll, + _sample_nitrate, + _sample_phosphate, + _sample_ph, + _sample_phytoplankton, + _sample_zooplankton, + _sample_primary_production, + _ctd_bgc_cast, + ], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, + ) + + # there should be no particles left, as they delete themselves when they resurface + if len(ctd_bgc_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before BGC CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + ) diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 70ac4b7b..d1e24390 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -1,7 +1,11 @@ from dataclasses import dataclass -from datetime import timedelta +from datetime import datetime, timedelta +from pathlib import Path from typing import ClassVar +import numpy as np +from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable + from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! @@ -18,9 +22,25 @@ class Drifter: lifetime: timedelta | None # if none, lifetime is infinite -# --------------- -# TODO: KERNELS -# --------------- +_DrifterParticle = JITParticle.add_variables( + [ + Variable("temperature", dtype=np.float32, initial=np.nan), + Variable("has_lifetime", dtype=np.int8), # bool + Variable("age", dtype=np.float32, initial=0.0), + Variable("lifetime", dtype=np.float32), + ] +) + + +def _sample_temperature(particle, fieldset, time): + particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] + + +def _check_lifetime(particle, fieldset, time): + if particle.has_lifetime == 1: + particle.age += particle.dt + if particle.age >= particle.lifetime: + particle.delete() class DrifterInputDataset(instruments.InputDataset): @@ -62,18 +82,91 @@ def get_datasets_dict(self) -> dict: } -class DrifterInstrument(instruments.Instrument): - """Drifter instrument class.""" - - def __init__( - self, - config, - input_dataset, - kernels, +# class DrifterInstrument(instruments.Instrument): +# """Drifter instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(Drifter.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_drifters( + fieldset: FieldSet, + out_path: str | Path, + drifters: list[Drifter], + outputdt: timedelta, + dt: timedelta, + endtime: datetime | None = None, +) -> None: + """ + Use Parcels to simulate a set of drifters in a fieldset. + + :param fieldset: The fieldset to simulate the Drifters in. + :param out_path: The path to write the results to. + :param drifters: A list of drifters to simulate. + :param outputdt: Interval which dictates the update frequency of file output during simulation. + :param dt: Dt for integration. + :param endtime: Stop at this time, or if None, continue until the end of the fieldset or until all drifters ended. If this is earlier than the last drifter ended or later than the end of the fieldset, a warning will be printed. + """ + if len(drifters) == 0: + print( + "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + # define parcel particles + drifter_particleset = ParticleSet( + fieldset=fieldset, + pclass=_DrifterParticle, + lat=[drifter.spacetime.location.lat for drifter in drifters], + lon=[drifter.spacetime.location.lon for drifter in drifters], + depth=[drifter.depth for drifter in drifters], + time=[drifter.spacetime.time for drifter in drifters], + has_lifetime=[1 if drifter.lifetime is not None else 0 for drifter in drifters], + lifetime=[ + 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() + for drifter in drifters + ], + ) + + # define output file for the simulation + out_file = drifter_particleset.ParticleFile( + name=out_path, outputdt=outputdt, chunks=[len(drifter_particleset), 100] + ) + + # get earliest between fieldset end time and provide end time + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + if endtime is None: + actual_endtime = fieldset_endtime + elif endtime > fieldset_endtime: + print("WARN: Requested end time later than fieldset end time.") + actual_endtime = fieldset_endtime + else: + actual_endtime = np.timedelta64(endtime) + + # execute simulation + drifter_particleset.execute( + [AdvectionRK4, _sample_temperature, _check_lifetime], + endtime=actual_endtime, + dt=dt, + output_file=out_file, + verbose_progress=True, + ) + + # if there are more particles left than the number of drifters with an indefinite endtime, warn the user + if len(drifter_particleset.particledata) > len( + [d for d in drifters if d.lifetime is None] ): - """Initialise with instrument's name.""" - super().__init__(Drifter.name, config, input_dataset, kernels) - - def simulate(self): - """Simulate measurements.""" - ... + print( + "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." + ) diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index f364b156..571a9ccd 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -1,7 +1,11 @@ from dataclasses import dataclass +from pathlib import Path from typing import ClassVar -from virtualship.models import instruments +import numpy as np +from parcels import FieldSet, ParticleSet, ScipyParticle, Variable + +from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! # + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py @@ -14,9 +18,22 @@ class Underwater_ST: name: ClassVar[str] = "Underwater_ST" -# --------------- -# TODO: KERNELS -# --------------- +_ShipSTParticle = ScipyParticle.add_variables( + [ + Variable("S", dtype=np.float32, initial=np.nan), + Variable("T", dtype=np.float32, initial=np.nan), + ] +) + + +# define function sampling Salinity +def _sample_salinity(particle, fieldset, time): + particle.S = fieldset.S[time, particle.depth, particle.lat, particle.lon] + + +# define function sampling Temperature +def _sample_temperature(particle, fieldset, time): + particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] class Underwater_STInputDataset(instruments.InputDataset): @@ -58,18 +75,67 @@ def get_datasets_dict(self) -> dict: } -class Underwater_STInstrument(instruments.Instrument): - """Underwater_ST instrument class.""" - - def __init__( - self, - config, - input_dataset, - kernels, - ): - """Initialise with instrument's name.""" - super().__init__(Underwater_ST.name, config, input_dataset, kernels) +# class Underwater_STInstrument(instruments.Instrument): +# """Underwater_ST instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(Underwater_ST.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_ship_underwater_st( + fieldset: FieldSet, + out_path: str | Path, + depth: float, + sample_points: list[Spacetime], +) -> None: + """ + Use Parcels to simulate underway data, measuring salinity and temperature at the given depth along the ship track in a fieldset. + + :param fieldset: The fieldset to simulate the sampling in. + :param out_path: The path to write the results to. + :param depth: The depth at which to measure. 0 is water surface, negative is into the water. + :param sample_points: The places and times to sample at. + """ + sample_points.sort(key=lambda p: p.time) + + particleset = ParticleSet.from_list( + fieldset=fieldset, + pclass=_ShipSTParticle, + lon=0.0, # initial lat/lon are irrelevant and will be overruled later + lat=0.0, + depth=depth, + time=0, # same for time + ) + + # define output file for the simulation + # outputdt set to infinie as we want to just want to write at the end of every call to 'execute' + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + + # iterate over each point, manually set lat lon time, then + # execute the particle set for one step, performing one set of measurement + for point in sample_points: + particleset.lon_nextloop[:] = point.location.lon + particleset.lat_nextloop[:] = point.location.lat + particleset.time_nextloop[:] = fieldset.time_origin.reltime( + np.datetime64(point.time) + ) - def simulate(self): - """Simulate measurements.""" - ... + # perform one step using the particleset + # dt and runtime are set so exactly one step is made. + particleset.execute( + [_sample_salinity, _sample_temperature], + dt=1, + runtime=1, + verbose_progress=False, + output_file=out_file, + ) diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index dcec018b..4d90f3f1 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -1,7 +1,11 @@ from dataclasses import dataclass from datetime import timedelta +from pathlib import Path from typing import ClassVar +import numpy as np +from parcels import FieldSet, JITParticle, ParticleSet, Variable + from virtualship.models import Spacetime, instruments ## TODO: __init__.py will also need updating! @@ -18,9 +22,37 @@ class XBT: lifetime: timedelta | None # if none, lifetime is infinite -# --------------- -# TODO: KERNELS -# --------------- +_XBTParticle = JITParticle.add_variables( + [ + Variable("temperature", dtype=np.float32, initial=np.nan), + Variable("max_depth", dtype=np.float32), + Variable("min_depth", dtype=np.float32), + Variable("fall_speed", dtype=np.float32), + Variable("deceleration_coefficient", dtype=np.float32), + ] +) + + +def _sample_temperature(particle, fieldset, time): + particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] + + +def _xbt_cast(particle, fieldset, time): + particle_ddepth = -particle.fall_speed * particle.dt + + # update the fall speed from the quadractic fall-rate equation + # check https://doi.org/10.5194/os-7-231-2011 + particle.fall_speed = ( + particle.fall_speed - 2 * particle.deceleration_coefficient * particle.dt + ) + + # delete particle if depth is exactly max_depth + if particle.depth == particle.max_depth: + particle.delete() + + # set particle depth to max depth if it's too deep + if particle.depth + particle_ddepth < particle.max_depth: + particle_ddepth = particle.max_depth - particle.depth class XBTInputDataset(instruments.InputDataset): @@ -67,18 +99,105 @@ def get_datasets_dict(self) -> dict: } -class XBTInstrument(instruments.Instrument): - """XBT instrument class.""" +# class XBTInstrument(instruments.Instrument): +# """XBT instrument class.""" + +# def __init__( +# self, +# config, +# input_dataset, +# kernels, +# ): +# """Initialise with instrument's name.""" +# super().__init__(XBT.name, config, input_dataset, kernels) + +# def simulate(self): +# """Simulate measurements.""" +# ... + + +def simulate_xbt( + fieldset: FieldSet, + out_path: str | Path, + xbts: list[XBT], + outputdt: timedelta, +) -> None: + """ + Use Parcels to simulate a set of XBTs in a fieldset. + + :param fieldset: The fieldset to simulate the XBTs in. + :param out_path: The path to write the results to. + :param xbts: A list of XBTs to simulate. + :param outputdt: Interval which dictates the update frequency of file output during simulation + :raises ValueError: Whenever provided XBTs, fieldset, are not compatible with this function. + """ + DT = 10.0 # dt of XBT simulation integrator + + if len(xbts) == 0: + print( + "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return - def __init__( - self, - config, - input_dataset, - kernels, - ): - """Initialise with instrument's name.""" - super().__init__(XBT.name, config, input_dataset, kernels) + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - def simulate(self): - """Simulate measurements.""" - ... + # deploy time for all xbts should be later than fieldset start time + if not all( + [np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts] + ): + raise ValueError("XBT deployed before fieldset starts.") + + # depth the xbt will go to. shallowest between xbt max depth and bathymetry. + max_depths = [ + max( + xbt.max_depth, + fieldset.bathymetry.eval( + z=0, y=xbt.spacetime.location.lat, x=xbt.spacetime.location.lon, time=0 + ), + ) + for xbt in xbts + ] + + # initial fall speeds + initial_fall_speeds = [xbt.fall_speed for xbt in xbts] + + # XBT depth can not be too shallow, because kernel would break. + # This shallow is not useful anyway, no need to support. + for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): + if not max_depth <= -DT * fall_speed: + raise ValueError( + f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}" + ) + + # define xbt particles + xbt_particleset = ParticleSet( + fieldset=fieldset, + pclass=_XBTParticle, + lon=[xbt.spacetime.location.lon for xbt in xbts], + lat=[xbt.spacetime.location.lat for xbt in xbts], + depth=[xbt.min_depth for xbt in xbts], + time=[xbt.spacetime.time for xbt in xbts], + max_depth=max_depths, + min_depth=[xbt.min_depth for xbt in xbts], + fall_speed=[xbt.fall_speed for xbt in xbts], + ) + + # define output file for the simulation + out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt) + + # execute simulation + xbt_particleset.execute( + [_sample_temperature, _xbt_cast], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, + ) + + # there should be no particles left, as they delete themselves when they finish profiling + if len(xbt_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before XBT finished profiling. This most likely means the field time dimension did not match the simulation time span." + ) From 8b5954d46b6075cd6b44b5bb4fb9570b4169ca7d Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:57:59 +0200 Subject: [PATCH 09/56] improve/clarify comments and notes --- src/virtualship/cli/_fetch.py | 6 ++++-- src/virtualship/cli/_plan.py | 1 - src/virtualship/instruments/ctd.py | 3 --- src/virtualship/instruments/drifter.py | 3 --- src/virtualship/instruments/master.py | 2 +- .../instruments/ship_underwater_st.py | 3 --- src/virtualship/models/instruments.py | 16 ++++------------ 7 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index c258f044..2a9be43e 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -63,7 +63,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.space_time_region ) - # TODO: this (below) probably needs updating! + # TODO: needs updating? existing_download = get_existing_download(data_dir, space_time_region_hash) if existing_download is not None: click.echo( @@ -106,9 +106,11 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None ) shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) + # TODO: enhance CLI output for users? + # bathymetry # TODO: this logic means it is downloaded for all expeditions but is only needed for CTD, CTD_BGC and XBT... - # TODO: to discuss: fine to still download for all expeditions because small size and then less duplication + # TODO: to discuss: fine to still download for all expeditions because small size and then less duplication? # TODO: or add as var in each of InputDataset objects per instrument because will be overwritten to disk anyway and therefore not duplicate? copernicusmarine.subset( dataset_id="cmems_mod_glo_phy_my_0.083deg_static", diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index 87bfe336..1aa3208b 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -847,7 +847,6 @@ def compose(self) -> ComposeResult: yield Select( [ (str(year), year) - # TODO: change from hard coding? ...flexibility for different datasets... for year in range( 2022, datetime.datetime.now().year + 1, diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 070cdb78..b2f59ac5 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -8,9 +8,6 @@ from virtualship.models import Spacetime, instruments -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class CTD: diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index d1e24390..8b0a1352 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -8,9 +8,6 @@ from virtualship.models import Spacetime, instruments -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class Drifter: diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 58eb0591..00f73891 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -6,7 +6,7 @@ class InstrumentType(Enum): # TODO: temporary measure so as not to have to overhaul the InstrumentType class logic in one go #! And also to avoid breaking other parts of the codebase which rely on InstrumentType when for now just working on fetch - # TODO: ideally this can evaporate in a future PR... + # TODO: ideally this can evaporate in the future... CTD = "CTD" CTD_BGC = "CTD_BGC" diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 571a9ccd..5fd39f2a 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -7,9 +7,6 @@ from virtualship.models import Spacetime, instruments -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class Underwater_ST: diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py index 7a356290..72d9237d 100644 --- a/src/virtualship/models/instruments.py +++ b/src/virtualship/models/instruments.py @@ -9,18 +9,10 @@ from virtualship.models.space_time_region import SpaceTimeRegion from virtualship.utils import ship_spinner -# TODO list START -# how much detail needs to be fed into InputDataset (i.e. how much it differs per instrument) -# may impact whether need a child class (e.g. CTDInputDataset) as well as just InputDataset -# or whether it could just be fed a `name` ... ? - -# ++ abc.abstractmethods could be useful for testing purposes...e.g. will fail if an instrumnet implementation doesn't adhere to the `Instrument` class standards - -# ++ discussion point with others, do we think it's okay to overhaul the data downloading so that each instrument has it's own files, rather than sharing data? -# ++ it's a cleaner way of making the whole repo more modular, i.e. have higher order logic for defining data downloads and housing all instrument logic in one place... -# ++ may even not matter so much considering working towards cloud integration... + we are not looking to optimise performance...? -# ++ OR, for now work on it in this way and then at the end make some clever changes to consolidate to minimum number of files dependent on instrument selections...? -# TODO list END +# TODO: +# Discussion: Should each instrument manage its own data files for modularity, +# or should we consolidate downloads to minimize file duplication across instruments? +# Consider starting with per-instrument files for simplicity, and refactor later if needed. class InputDataset(abc.ABC): From ff38f4f261a03e4018a35ce67020b70b3a27d259 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:58:25 +0200 Subject: [PATCH 10/56] Refactor ArgoFloat and XBT classes to include depth parameters and remove outdated comments --- src/virtualship/instruments/argo_float.py | 11 ++++++----- src/virtualship/instruments/xbt.py | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 4769eccf..a9f296d5 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -16,9 +16,6 @@ from virtualship.models import Spacetime, instruments -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class ArgoFloat: @@ -26,8 +23,12 @@ class ArgoFloat: name: ClassVar[str] = "ArgoFloat" spacetime: Spacetime - depth: float # depth at which it floats and samples - lifetime: timedelta | None # if none, lifetime is infinite + min_depth: float + max_depth: float + drift_depth: float + vertical_speed: float + cycle_days: float + drift_days: float _ArgoParticle = JITParticle.add_variables( diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 4d90f3f1..ca831afc 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -8,9 +8,6 @@ from virtualship.models import Spacetime, instruments -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class XBT: @@ -18,8 +15,10 @@ class XBT: name: ClassVar[str] = "XBT" spacetime: Spacetime - depth: float # depth at which it floats and samples - lifetime: timedelta | None # if none, lifetime is infinite + min_depth: float + max_depth: float + fall_speed: float + deceleration_coefficient: float _XBTParticle = JITParticle.add_variables( @@ -165,6 +164,7 @@ def simulate_xbt( # XBT depth can not be too shallow, because kernel would break. # This shallow is not useful anyway, no need to support. + # TODO: should this be more informative? Is "maximum" right? Should tell user can't use XBT here? for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): if not max_depth <= -DT * fall_speed: raise ValueError( From c870d1cbcf5c218cb9d8637acb45bca045c0435a Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:59:25 +0200 Subject: [PATCH 11/56] avoid circular import issues --- src/virtualship/instruments/__init__.py | 2 ++ src/virtualship/instruments/adcp.py | 5 +++-- src/virtualship/instruments/argo_float.py | 5 +++-- src/virtualship/instruments/ctd.py | 5 +++-- src/virtualship/instruments/ctd_bgc.py | 5 +++-- src/virtualship/instruments/drifter.py | 5 +++-- src/virtualship/instruments/ship_underwater_st.py | 5 +++-- src/virtualship/instruments/xbt.py | 5 +++-- 8 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/virtualship/instruments/__init__.py b/src/virtualship/instruments/__init__.py index 6a6ffbca..a5da8dac 100644 --- a/src/virtualship/instruments/__init__.py +++ b/src/virtualship/instruments/__init__.py @@ -1,5 +1,7 @@ """Measurement instrument that can be used with Parcels.""" +from virtualship.models.spacetime import Spacetime # noqa: F401 + from . import adcp, argo_float, ctd, ctd_bgc, drifter, ship_underwater_st, xbt __all__ = [ diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 052004fb..ccf48738 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -5,7 +5,8 @@ import numpy as np from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime ## TODO: __init__.py will also need updating! # + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py @@ -34,7 +35,7 @@ def _sample_velocity(particle, fieldset, time): ) -class ADCPInputDataset(instruments.InputDataset): +class ADCPInputDataset(InputDataset): """Input dataset for ADCP instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index a9f296d5..b9949fb1 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -14,7 +14,8 @@ Variable, ) -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -115,7 +116,7 @@ def _check_error(particle, fieldset, time): particle.delete() -class ArgoFloatInputDataset(instruments.InputDataset): +class ArgoFloatInputDataset(InputDataset): """Input dataset for ArgoFloat instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index b2f59ac5..15a46234 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -6,7 +6,8 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -53,7 +54,7 @@ def _ctd_cast(particle, fieldset, time): particle.delete() -class CTDInputDataset(instruments.InputDataset): +class CTDInputDataset(InputDataset): """Input dataset for CTD instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 1025a5c8..fb9ea241 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -6,7 +6,8 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -83,7 +84,7 @@ def _ctd_bgc_cast(particle, fieldset, time): particle.delete() -class CTD_BGCInputDataset(instruments.InputDataset): +class CTD_BGCInputDataset(InputDataset): """Input dataset object for CTD_BGC instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 8b0a1352..60ba5558 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -6,7 +6,8 @@ import numpy as np from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -40,7 +41,7 @@ def _check_lifetime(particle, fieldset, time): particle.delete() -class DrifterInputDataset(instruments.InputDataset): +class DrifterInputDataset(InputDataset): """Input dataset for Drifter instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 5fd39f2a..332c0b2c 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -5,7 +5,8 @@ import numpy as np from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -33,7 +34,7 @@ def _sample_temperature(particle, fieldset, time): particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] -class Underwater_STInputDataset(instruments.InputDataset): +class Underwater_STInputDataset(InputDataset): """Input dataset for Underwater_ST instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index ca831afc..81cba7b9 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -6,7 +6,8 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models import Spacetime, instruments +from virtualship.models.instruments import InputDataset +from virtualship.models.spacetime import Spacetime @dataclass @@ -54,7 +55,7 @@ def _xbt_cast(particle, fieldset, time): particle_ddepth = particle.max_depth - particle.depth -class XBTInputDataset(instruments.InputDataset): +class XBTInputDataset(InputDataset): """Input dataset for XBT instrument.""" DOWNLOAD_BUFFERS: ClassVar[dict] = { From 49b3beeb44f8938e16398828f8a1448384002d17 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:01:27 +0200 Subject: [PATCH 12/56] make tests for InputDataset base class --- tests/models/test_instruments.py | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/models/test_instruments.py diff --git a/tests/models/test_instruments.py b/tests/models/test_instruments.py new file mode 100644 index 00000000..2ad73cdf --- /dev/null +++ b/tests/models/test_instruments.py @@ -0,0 +1,97 @@ +import datetime +from unittest.mock import patch + +import pytest + +from virtualship.models.instruments import InputDataset +from virtualship.models.space_time_region import ( + SpaceTimeRegion, + SpatialRange, + TimeRange, +) + + +class DummyInputDataset(InputDataset): + """A minimal InputDataset subclass for testing purposes.""" + + def get_datasets_dict(self): + """Return a dummy datasets dict for testing.""" + return { + "dummy": { + "dataset_id": "test_id", + "variables": ["var1"], + "output_filename": "dummy.nc", + } + } + + +@pytest.fixture +def dummy_space_time_region(): + spatial_range = SpatialRange( + minimum_longitude=0, + maximum_longitude=1, + minimum_latitude=0, + maximum_latitude=1, + minimum_depth=0, + maximum_depth=10, + ) + base_time = datetime.datetime.strptime("1950-01-01", "%Y-%m-%d") + time_range = TimeRange( + start_time=base_time, + end_time=base_time + datetime.timedelta(hours=1), + ) + return SpaceTimeRegion( + spatial_range=spatial_range, + time_range=time_range, + ) + + +def test_inputdataset_abstract_instantiation(): + # instantiation should not be allowed + with pytest.raises(TypeError): + InputDataset( + name="test", + latlon_buffer=0, + datetime_buffer=0, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=None, + ) + + +def test_dummyinputdataset_initialization(dummy_space_time_region): + ds = DummyInputDataset( + name="test", + latlon_buffer=0.5, + datetime_buffer=1, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=dummy_space_time_region, + ) + assert ds.name == "test" + assert ds.latlon_buffer == 0.5 + assert ds.datetime_buffer == 1 + assert ds.min_depth == 0 + assert ds.max_depth == 10 + assert ds.data_dir == "." + assert ds.credentials["username"] == "u" + + +@patch("virtualship.models.instruments.copernicusmarine.subset") +def test_download_data_calls_subset(mock_subset, dummy_space_time_region): + ds = DummyInputDataset( + name="test", + latlon_buffer=0.5, + datetime_buffer=1, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=dummy_space_time_region, + ) + ds.download_data() + assert mock_subset.called From c4e31963d8d23f71c6e051228ff0b70cff3f8c0d Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:40:39 +0200 Subject: [PATCH 13/56] refactor instrument handling in _fetch.py and update expedition model to include get_instruments method --- src/virtualship/cli/_fetch.py | 149 ++---------------------- src/virtualship/instruments/__init__.py | 16 +-- src/virtualship/instruments/master.py | 4 +- src/virtualship/models/__init__.py | 2 - src/virtualship/models/expedition.py | 39 +++---- 5 files changed, 30 insertions(+), 180 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 2a9be43e..c460db37 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -11,6 +11,7 @@ from pydantic import BaseModel from virtualship.errors import IncompleteDownloadError +from virtualship.instruments.master import get_instruments_registry from virtualship.utils import ( _dump_yaml, _generic_load_yaml, @@ -24,8 +25,6 @@ import virtualship.cli._creds as creds from virtualship.utils import EXPEDITION -from virtualship.instruments.master import INSTRUMENTS -from virtualship.instruments.master import InstrumentType, get_instruments_registry DOWNLOAD_METADATA = "download_metadata.yaml" @@ -63,7 +62,6 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.space_time_region ) - # TODO: needs updating? existing_download = get_existing_download(data_dir, space_time_region_hash) if existing_download is not None: click.echo( @@ -77,26 +75,9 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None username, password, creds_path ) - # Extract space_time_region details from the schedule - spatial_range = expedition.schedule.space_time_region.spatial_range - time_range = expedition.schedule.space_time_region.time_range - start_datetime = time_range.start_time - end_datetime = time_range.end_time - instruments_in_schedule = expedition.schedule.get_instruments() - - # TEMPORARY measure to get underway instruments in `instruments_in_schedule` - # TODO: should evaporate when schedule and ship_config.yaml files are consolidated in a separate PR... - if ship_config.adcp_config is not None: - instruments_in_schedule.add(InstrumentType.ADCP) - if ship_config.ship_underwater_st_config is not None: - instruments_in_schedule.add(InstrumentType.UNDERWATER_ST) - - # TEMPORARY measure to get underway instruments in `instruments_in_schedule` - # TODO: should evaporate when schedule and ship_config.yaml files are consolidated in a separate PR... - if ship_config.adcp_config is not None: - instruments_in_schedule.add(InstrumentType.ADCP) - if ship_config.ship_underwater_st_config is not None: - instruments_in_schedule.add(InstrumentType.UNDERWATER_ST) + # Extract instruments and space_time_region details from expedition + instruments_in_expedition = expedition.get_instruments() + space_time_region = expedition.schedule.space_time_region # Create download folder and set download metadata download_folder = data_dir / hash_to_filename(space_time_region_hash) @@ -108,10 +89,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None # TODO: enhance CLI output for users? - # bathymetry - # TODO: this logic means it is downloaded for all expeditions but is only needed for CTD, CTD_BGC and XBT... - # TODO: to discuss: fine to still download for all expeditions because small size and then less duplication? - # TODO: or add as var in each of InputDataset objects per instrument because will be overwritten to disk anyway and therefore not duplicate? + # bathymetry (for all expeditions) copernicusmarine.subset( dataset_id="cmems_mod_glo_phy_my_0.083deg_static", variables=["deptho"], @@ -131,9 +109,9 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None coordinates_selection_method="outside", ) - # keep only instruments in INTSTRUMENTS which are in schedule + # access instrument classes but keep only instruments which are in schedule filter_instruments = { - k: v for k, v in INSTRUMENTS.items() if k in instruments_in_schedule + k: v for k, v in INSTRUMENTS.items() if k in instruments_in_expedition } # iterate across instruments and download data based on space_time_region @@ -142,116 +120,11 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None input_dataset = instrument["input_class"]( data_dir=download_folder, credentials=credentials, - space_time_region=space_time_region, + space_time_region=expedition.space_time_region, ) - #! - #### TODO - # ++ new logic here where iterates (?) through available instruments and determines whether download is required: - # ++ by conditions of: - # 1) whether it's in the schedule (and from this be able to call the right classes from the instruments directory?) and - #! 2) is there a clever way of not unnecessarily duplicating data downloads if instruments use the same?! - # (try with a version first where does them all in tow and then try and optimise...?) - - #! - ## TODO: move to generic bathymetry download which is done for all expeditions - - if ( - ( - {"XBT", "CTD", "CDT_BGC", "SHIP_UNDERWATER_ST"} - & set(instrument.name for instrument in instruments_in_schedule) - ) - or expedition.instruments_config.ship_underwater_st_config is not None - or expedition.instruments_config.adcp_config is not None - ): - print("Ship data will be downloaded. Please wait...") - - # Define all ship datasets to download, including bathymetry - download_dict = { - "Bathymetry": { - "dataset_id": "cmems_mod_glo_phy_my_0.083deg_static", - "variables": ["deptho"], - "output_filename": "bathymetry.nc", - }, - "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", - "variables": ["uo", "vo"], - "output_filename": "ship_uv.nc", - }, - "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", - "variables": ["so"], - "output_filename": "ship_s.nc", - }, - "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", - "variables": ["thetao"], - "output_filename": "ship_t.nc", - }, - } - - # Iterate over all datasets and download each based on space_time_region - try: - for dataset in download_dict.values(): - copernicusmarine.subset( - dataset_id=dataset["dataset_id"], - variables=dataset["variables"], - minimum_longitude=spatial_range.minimum_longitude, - maximum_longitude=spatial_range.maximum_longitude, - minimum_latitude=spatial_range.minimum_latitude, - maximum_latitude=spatial_range.maximum_latitude, - start_datetime=start_datetime, - end_datetime=end_datetime, - minimum_depth=abs(spatial_range.minimum_depth), - maximum_depth=abs(spatial_range.maximum_depth), - output_filename=dataset["output_filename"], - output_directory=download_folder, - username=username, - password=password, - overwrite=True, - coordinates_selection_method="outside", - ) - except InvalidUsernameOrPassword as e: - shutil.rmtree(download_folder) - raise e - click.echo("Ship data download based on space-time region completed.") - - if InstrumentType.DRIFTER in instruments_in_schedule: - print("Drifter data will be downloaded. Please wait...") - drifter_download_dict = { - "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", - "variables": ["uo", "vo"], - "output_filename": "drifter_uv.nc", - }, - "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", - "variables": ["thetao"], - "output_filename": "drifter_t.nc", - }, - } - - # Iterate over all datasets and download each based on space_time_region - try: - for dataset in drifter_download_dict.values(): - copernicusmarine.subset( - dataset_id=dataset["dataset_id"], - variables=dataset["variables"], - minimum_longitude=spatial_range.minimum_longitude - 3.0, - maximum_longitude=spatial_range.maximum_longitude + 3.0, - minimum_latitude=spatial_range.minimum_latitude - 3.0, - maximum_latitude=spatial_range.maximum_latitude + 3.0, - start_datetime=start_datetime, - end_datetime=end_datetime + timedelta(days=21), - minimum_depth=abs(1), - maximum_depth=abs(1), - output_filename=dataset["output_filename"], - output_directory=download_folder, - username=username, - password=password, - overwrite=True, - coordinates_selection_method="outside", - ) + input_dataset.download_data() + except InvalidUsernameOrPassword as e: shutil.rmtree(download_folder) raise e @@ -331,11 +204,9 @@ def get_existing_download(data_dir: Path, space_time_region_hash: str) -> Path | hash = filename_to_hash(download_path.name) except ValueError: continue - if hash == space_time_region_hash: assert_complete_download(download_path) return download_path - return None diff --git a/src/virtualship/instruments/__init__.py b/src/virtualship/instruments/__init__.py index a5da8dac..5324da2c 100644 --- a/src/virtualship/instruments/__init__.py +++ b/src/virtualship/instruments/__init__.py @@ -1,15 +1 @@ -"""Measurement instrument that can be used with Parcels.""" - -from virtualship.models.spacetime import Spacetime # noqa: F401 - -from . import adcp, argo_float, ctd, ctd_bgc, drifter, ship_underwater_st, xbt - -__all__ = [ - "adcp", - "argo_float", - "ctd", - "ctd_bgc", - "drifter", - "ship_underwater_st", - "xbt", -] +"""Instruments in VirtualShip.""" diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 00f73891..cb31468f 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -4,9 +4,7 @@ class InstrumentType(Enum): """Types of the instruments.""" - # TODO: temporary measure so as not to have to overhaul the InstrumentType class logic in one go - #! And also to avoid breaking other parts of the codebase which rely on InstrumentType when for now just working on fetch - # TODO: ideally this can evaporate in the future... + # TODO: scope for this to evaporate in the future...? CTD = "CTD" CTD_BGC = "CTD_BGC" diff --git a/src/virtualship/models/__init__.py b/src/virtualship/models/__init__.py index a2f1546c..5eaabb85 100644 --- a/src/virtualship/models/__init__.py +++ b/src/virtualship/models/__init__.py @@ -8,7 +8,6 @@ DrifterConfig, Expedition, InstrumentsConfig, - InstrumentType, Schedule, ShipConfig, ShipUnderwaterSTConfig, @@ -30,7 +29,6 @@ "Schedule", "ShipConfig", "Waypoint", - "InstrumentType", "ArgoFloatConfig", "ADCPConfig", "CTDConfig", diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 2e073b84..ef860625 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -2,7 +2,6 @@ import itertools from datetime import datetime, timedelta -from enum import Enum from typing import TYPE_CHECKING import pydantic @@ -10,6 +9,7 @@ import yaml from virtualship.errors import ConfigError, ScheduleError +from virtualship.instruments.master import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta from .location import Location @@ -45,6 +45,23 @@ def from_yaml(cls, file_path: str) -> Expedition: data = yaml.safe_load(file) return Expedition(**data) + def get_instruments(self) -> set[InstrumentType]: + """Return a set of unique InstrumentType enums used in the expedition.""" + instruments_in_expedition = [] + # from waypoints + for waypoint in self.schedule.waypoints: + if waypoint.instrument: + for instrument in waypoint.instrument: + if instrument: + instruments_in_expedition.append(instrument) + # check for underway instruments and add if present in expeditions + if self.instruments_config.adcp_config is not None: + instruments_in_expedition.append(InstrumentType.ADCP) + if self.instruments_config.ship_underwater_st_config is not None: + instruments_in_expedition.append(InstrumentType.UNDERWATER_ST) + + return set(instruments_in_expedition) + class ShipConfig(pydantic.BaseModel): """Configuration of the ship.""" @@ -64,16 +81,6 @@ class Schedule(pydantic.BaseModel): model_config = pydantic.ConfigDict(extra="forbid") - def get_instruments(self) -> set[InstrumentType]: - """Return a set of unique InstrumentType enums used in the schedule.""" - instruments_in_schedule = [] - for waypoint in self.waypoints: - if waypoint.instrument: - for instrument in waypoint.instrument: - if instrument: - instruments_in_schedule.append(instrument) - return set(instruments_in_schedule) - def verify( self, ship_speed: float, @@ -213,16 +220,6 @@ def serialize_instrument(self, instrument): return instrument.value if instrument else None -class InstrumentType(Enum): - """Types of the instruments.""" - - CTD = "CTD" - CTD_BGC = "CTD_BGC" - DRIFTER = "DRIFTER" - ARGO_FLOAT = "ARGO_FLOAT" - XBT = "XBT" - - class ArgoFloatConfig(pydantic.BaseModel): """Configuration for argos floats.""" From 2f40f7dff5755e5e173800e8ee81a95ab5640fdf Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:21:02 +0200 Subject: [PATCH 14/56] refactor instrument error handling in Expedition model and remove Schedule and ShipConfig classes --- src/virtualship/models/expedition.py | 37 +-- src/virtualship/models/schedule.py | 236 -------------------- src/virtualship/models/ship_config.py | 310 -------------------------- 3 files changed, 24 insertions(+), 559 deletions(-) delete mode 100644 src/virtualship/models/schedule.py delete mode 100644 src/virtualship/models/ship_config.py diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index ef860625..7e206e8f 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -8,7 +8,7 @@ import pyproj import yaml -from virtualship.errors import ConfigError, ScheduleError +from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.master import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta @@ -54,13 +54,18 @@ def get_instruments(self) -> set[InstrumentType]: for instrument in waypoint.instrument: if instrument: instruments_in_expedition.append(instrument) - # check for underway instruments and add if present in expeditions - if self.instruments_config.adcp_config is not None: - instruments_in_expedition.append(InstrumentType.ADCP) - if self.instruments_config.ship_underwater_st_config is not None: - instruments_in_expedition.append(InstrumentType.UNDERWATER_ST) - return set(instruments_in_expedition) + # check for underway instruments and add if present in expeditions + try: + if self.instruments_config.adcp_config is not None: + instruments_in_expedition.append(InstrumentType.ADCP) + if self.instruments_config.ship_underwater_st_config is not None: + instruments_in_expedition.append(InstrumentType.UNDERWATER_ST) + return set(instruments_in_expedition) + except Exception as e: + raise InstrumentsConfigError( + "Underway instrument config attribute(s) are missing from YAML. Must be Config object or None." + ) from e class ShipConfig(pydantic.BaseModel): @@ -348,6 +353,7 @@ class XBTConfig(pydantic.BaseModel): class InstrumentsConfig(pydantic.BaseModel): + # TODO: refactor potential for this? Move explicit instrument_config's away from models/ dir? """Configuration of instruments.""" argo_float_config: ArgoFloatConfig | None = None @@ -401,38 +407,43 @@ class InstrumentsConfig(pydantic.BaseModel): model_config = pydantic.ConfigDict(extra="forbid") - def verify(self, schedule: Schedule) -> None: + def verify(self, expedition: Expedition) -> None: """ Verify instrument configurations against the schedule. Removes instrument configs not present in the schedule and checks that all scheduled instruments are configured. Raises ConfigError if any scheduled instrument is missing a config. """ - instruments_in_schedule = schedule.get_instruments() + instruments_in_expedition = expedition.get_instruments() instrument_config_map = { InstrumentType.ARGO_FLOAT: "argo_float_config", InstrumentType.DRIFTER: "drifter_config", InstrumentType.XBT: "xbt_config", InstrumentType.CTD: "ctd_config", InstrumentType.CTD_BGC: "ctd_bgc_config", + InstrumentType.ADCP: "adcp_config", + InstrumentType.UNDERWATER_ST: "ship_underwater_st_config", } # Remove configs for unused instruments for inst_type, config_attr in instrument_config_map.items(): - if hasattr(self, config_attr) and inst_type not in instruments_in_schedule: + if ( + hasattr(self, config_attr) + and inst_type not in instruments_in_expedition + ): print( f"{inst_type.value} configuration provided but not in schedule. Removing config." ) setattr(self, config_attr, None) # Check all scheduled instruments are configured - for inst_type in instruments_in_schedule: + for inst_type in instruments_in_expedition: config_attr = instrument_config_map.get(inst_type) if ( not config_attr or not hasattr(self, config_attr) or getattr(self, config_attr) is None ): - raise ConfigError( - f"Schedule includes instrument '{inst_type.value}', but instruments_config does not provide configuration for it." + raise InstrumentsConfigError( + f"Expedition includes instrument '{inst_type.value}', but instruments_config does not provide configuration for it." ) diff --git a/src/virtualship/models/schedule.py b/src/virtualship/models/schedule.py deleted file mode 100644 index f3e5dabe..00000000 --- a/src/virtualship/models/schedule.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Schedule class.""" - -from __future__ import annotations - -import itertools -from datetime import datetime, timedelta -from pathlib import Path -from typing import TYPE_CHECKING - -import pydantic -import pyproj -import yaml - -from virtualship.errors import ScheduleError -from virtualship.instruments.master import InstrumentType - -from .location import Location -from .space_time_region import SpaceTimeRegion - -if TYPE_CHECKING: - from parcels import FieldSet - - from virtualship.expedition.input_data import InputData - -projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") - - -class Waypoint(pydantic.BaseModel): - """A Waypoint to sail to with an optional time and an optional instrument.""" - - location: Location - time: datetime | None = None - instrument: InstrumentType | list[InstrumentType] | None = None - - @pydantic.field_serializer("instrument") - def serialize_instrument(self, instrument): - """Ensure InstrumentType is serialized as a string (or list of strings).""" - if isinstance(instrument, list): - return [inst.value for inst in instrument] - return instrument.value if instrument else None - - -class Schedule(pydantic.BaseModel): - """Schedule of the virtual ship.""" - - waypoints: list[Waypoint] - space_time_region: SpaceTimeRegion | None = None - - model_config = pydantic.ConfigDict(extra="forbid") - - def to_yaml(self, file_path: str | Path) -> None: - """ - Write schedule to yaml file. - - :param file_path: Path to the file to write to. - """ - with open(file_path, "w") as file: - yaml.dump( - self.model_dump( - by_alias=True, - ), - file, - ) - - @classmethod - def from_yaml(cls, file_path: str | Path) -> Schedule: - """ - Load schedule from yaml file. - - :param file_path: Path to the file to load from. - :returns: The schedule. - """ - with open(file_path) as file: - data = yaml.safe_load(file) - return Schedule(**data) - - def get_instruments(self) -> set[InstrumentType]: - """ - Retrieve a set of unique instruments used in the schedule. - - This method iterates through all waypoints in the schedule and collects - the instruments associated with each waypoint. It returns a set of unique - instruments, either as objects or as names. - - :raises CheckpointError: If the past waypoints in the given schedule - have been changed compared to the checkpoint. - :return: set: A set of unique instruments used in the schedule. - - """ - instruments_in_schedule = [] - for waypoint in self.waypoints: - if waypoint.instrument: - for instrument in waypoint.instrument: - if instrument: - instruments_in_schedule.append(instrument) - return set(instruments_in_schedule) - - def verify( - self, - ship_speed: float, - input_data: InputData | None, - *, - check_space_time_region: bool = False, - ignore_missing_fieldsets: bool = False, - ) -> None: - """ - Verify the feasibility and correctness of the schedule's waypoints. - - This method checks various conditions to ensure the schedule is valid: - 1. At least one waypoint is provided. - 2. The first waypoint has a specified time. - 3. Waypoint times are in ascending order. - 4. All waypoints are in water (not on land). - 5. The ship can arrive on time at each waypoint given its speed. - - :param ship_speed: The ship's speed in knots. - :param input_data: An InputData object containing fieldsets used to check if waypoints are on water. - :param check_space_time_region: whether to check for missing space_time_region. - :param ignore_missing_fieldsets: whether to ignore warning for missing field sets. - :raises PlanningError: If any of the verification checks fail, indicating infeasible or incorrect waypoints. - :raises NotImplementedError: If an instrument in the schedule is not implemented. - :return: None. The method doesn't return a value but raises exceptions if verification fails. - """ - print("\nVerifying route... ") - - if check_space_time_region and self.space_time_region is None: - raise ScheduleError( - "space_time_region not found in schedule, please define it to fetch the data." - ) - - if len(self.waypoints) == 0: - raise ScheduleError("At least one waypoint must be provided.") - - # check first waypoint has a time - if self.waypoints[0].time is None: - raise ScheduleError("First waypoint must have a specified time.") - - # check waypoint times are in ascending order - timed_waypoints = [wp for wp in self.waypoints if wp.time is not None] - checks = [ - next.time >= cur.time for cur, next in itertools.pairwise(timed_waypoints) - ] - if not all(checks): - invalid_i = [i for i, c in enumerate(checks) if c] - raise ScheduleError( - f"Waypoint(s) {', '.join(f'#{i + 1}' for i in invalid_i)}: each waypoint should be timed after all previous waypoints", - ) - - # check if all waypoints are in water - # this is done by picking an arbitrary provided fieldset and checking if UV is not zero - - # get all available fieldsets - available_fieldsets = [] - if input_data is not None: - fieldsets = [ - input_data.adcp_fieldset, - input_data.argo_float_fieldset, - input_data.ctd_fieldset, - input_data.drifter_fieldset, - input_data.ship_underwater_st_fieldset, - ] - for fs in fieldsets: - if fs is not None: - available_fieldsets.append(fs) - - # check if there are any fieldsets, else it's an error - if len(available_fieldsets) == 0: - if not ignore_missing_fieldsets: - print( - "Cannot verify because no fieldsets have been loaded. This is probably " - "because you are not using any instruments in your schedule. This is not a problem, " - "but carefully check your waypoint locations manually." - ) - - else: - # pick any - fieldset = available_fieldsets[0] - # get waypoints with 0 UV - land_waypoints = [ - (wp_i, wp) - for wp_i, wp in enumerate(self.waypoints) - if _is_on_land_zero_uv(fieldset, wp) - ] - # raise an error if there are any - if len(land_waypoints) > 0: - raise ScheduleError( - f"The following waypoints are on land: {['#' + str(wp_i) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" - ) - - # check that ship will arrive on time at each waypoint (in case no unexpected event happen) - time = self.waypoints[0].time - for wp_i, (wp, wp_next) in enumerate( - zip(self.waypoints, self.waypoints[1:], strict=False) - ): - if wp.instrument is InstrumentType.CTD: - time += timedelta(minutes=20) - - geodinv: tuple[float, float, float] = projection.inv( - wp.location.lon, - wp.location.lat, - wp_next.location.lon, - wp_next.location.lat, - ) - distance = geodinv[2] - - time_to_reach = timedelta(seconds=distance / ship_speed * 3600 / 1852) - arrival_time = time + time_to_reach - - if wp_next.time is None: - time = arrival_time - elif arrival_time > wp_next.time: - raise ScheduleError( - f"Waypoint planning is not valid: would arrive too late at waypoint number {wp_i + 2}. " - f"location: {wp_next.location} time: {wp_next.time} instrument: {wp_next.instrument}" - ) - else: - time = wp_next.time - - print("... All good to go!") - - -def _is_on_land_zero_uv(fieldset: FieldSet, waypoint: Waypoint) -> bool: - """ - Check if waypoint is on land by assuming zero velocity means land. - - :param fieldset: The fieldset to sample the velocity from. - :param waypoint: The waypoint to check. - :returns: If the waypoint is on land. - """ - return fieldset.UV.eval( - 0, - fieldset.gridset.grids[0].depth[0], - waypoint.location.lat, - waypoint.location.lon, - applyConversion=False, - ) == (0.0, 0.0) diff --git a/src/virtualship/models/ship_config.py b/src/virtualship/models/ship_config.py deleted file mode 100644 index ba7d221f..00000000 --- a/src/virtualship/models/ship_config.py +++ /dev/null @@ -1,310 +0,0 @@ -"""ShipConfig and supporting classes.""" - -from __future__ import annotations - -from datetime import timedelta -from pathlib import Path -from typing import TYPE_CHECKING - -import pydantic -import yaml - -from virtualship.errors import ConfigError -from virtualship.instruments.master import InstrumentType -from virtualship.utils import _validate_numeric_mins_to_timedelta - -if TYPE_CHECKING: - from .schedule import Schedule - - -class ArgoFloatConfig(pydantic.BaseModel): - """Configuration for argos floats.""" - - min_depth_meter: float = pydantic.Field(le=0.0) - max_depth_meter: float = pydantic.Field(le=0.0) - drift_depth_meter: float = pydantic.Field(le=0.0) - vertical_speed_meter_per_second: float = pydantic.Field(lt=0.0) - cycle_days: float = pydantic.Field(gt=0.0) - drift_days: float = pydantic.Field(gt=0.0) - - -class ADCPConfig(pydantic.BaseModel): - """Configuration for ADCP instrument.""" - - max_depth_meter: float = pydantic.Field(le=0.0) - num_bins: int = pydantic.Field(gt=0.0) - period: timedelta = pydantic.Field( - serialization_alias="period_minutes", - validation_alias="period_minutes", - gt=timedelta(), - ) - - model_config = pydantic.ConfigDict(populate_by_name=True) - - @pydantic.field_serializer("period") - def _serialize_period(self, value: timedelta, _info): - return value.total_seconds() / 60.0 - - @pydantic.field_validator("period", mode="before") - def _validate_period(cls, value: int | float | timedelta) -> timedelta: - return _validate_numeric_mins_to_timedelta(value) - - -class CTDConfig(pydantic.BaseModel): - """Configuration for CTD instrument.""" - - stationkeeping_time: timedelta = pydantic.Field( - serialization_alias="stationkeeping_time_minutes", - validation_alias="stationkeeping_time_minutes", - gt=timedelta(), - ) - min_depth_meter: float = pydantic.Field(le=0.0) - max_depth_meter: float = pydantic.Field(le=0.0) - - model_config = pydantic.ConfigDict(populate_by_name=True) - - @pydantic.field_serializer("stationkeeping_time") - def _serialize_stationkeeping_time(self, value: timedelta, _info): - return value.total_seconds() / 60.0 - - @pydantic.field_validator("stationkeeping_time", mode="before") - def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta: - return _validate_numeric_mins_to_timedelta(value) - - -class CTD_BGCConfig(pydantic.BaseModel): - """Configuration for CTD_BGC instrument.""" - - stationkeeping_time: timedelta = pydantic.Field( - serialization_alias="stationkeeping_time_minutes", - validation_alias="stationkeeping_time_minutes", - gt=timedelta(), - ) - min_depth_meter: float = pydantic.Field(le=0.0) - max_depth_meter: float = pydantic.Field(le=0.0) - - model_config = pydantic.ConfigDict(populate_by_name=True) - - @pydantic.field_serializer("stationkeeping_time") - def _serialize_stationkeeping_time(self, value: timedelta, _info): - return value.total_seconds() / 60.0 - - @pydantic.field_validator("stationkeeping_time", mode="before") - def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta: - return _validate_numeric_mins_to_timedelta(value) - - -class ShipUnderwaterSTConfig(pydantic.BaseModel): - """Configuration for underwater ST.""" - - period: timedelta = pydantic.Field( - serialization_alias="period_minutes", - validation_alias="period_minutes", - gt=timedelta(), - ) - - model_config = pydantic.ConfigDict(populate_by_name=True) - - @pydantic.field_serializer("period") - def _serialize_period(self, value: timedelta, _info): - return value.total_seconds() / 60.0 - - @pydantic.field_validator("period", mode="before") - def _validate_period(cls, value: int | float | timedelta) -> timedelta: - return _validate_numeric_mins_to_timedelta(value) - - -class DrifterConfig(pydantic.BaseModel): - """Configuration for drifters.""" - - depth_meter: float = pydantic.Field(le=0.0) - lifetime: timedelta = pydantic.Field( - serialization_alias="lifetime_minutes", - validation_alias="lifetime_minutes", - gt=timedelta(), - ) - - model_config = pydantic.ConfigDict(populate_by_name=True) - - @pydantic.field_serializer("lifetime") - def _serialize_lifetime(self, value: timedelta, _info): - return value.total_seconds() / 60.0 - - @pydantic.field_validator("lifetime", mode="before") - def _validate_lifetime(cls, value: int | float | timedelta) -> timedelta: - return _validate_numeric_mins_to_timedelta(value) - - -class XBTConfig(pydantic.BaseModel): - """Configuration for xbt instrument.""" - - min_depth_meter: float = pydantic.Field(le=0.0) - max_depth_meter: float = pydantic.Field(le=0.0) - fall_speed_meter_per_second: float = pydantic.Field(gt=0.0) - deceleration_coefficient: float = pydantic.Field(gt=0.0) - - -class ShipConfig(pydantic.BaseModel): - """Configuration of the virtual ship.""" - - ship_speed_knots: float = pydantic.Field(gt=0.0) - """ - Velocity of the ship in knots. - """ - - argo_float_config: ArgoFloatConfig | None = None - """ - Argo float configuration. - - If None, no argo floats can be deployed. - """ - - adcp_config: ADCPConfig | None = None - """ - ADCP configuration. - - If None, no ADCP measurements will be performed. - """ - - ctd_config: CTDConfig | None = None - """ - CTD configuration. - - If None, no CTDs can be cast. - """ - - ctd_bgc_config: CTD_BGCConfig | None = None - """ - CTD_BGC configuration. - - If None, no BGC CTDs can be cast. - """ - - ship_underwater_st_config: ShipUnderwaterSTConfig | None = None - """ - Ship underwater salinity temperature measurementconfiguration. - - If None, no ST measurements will be performed. - """ - - drifter_config: DrifterConfig | None = None - """ - Drifter configuration. - - If None, no drifters can be deployed. - """ - - xbt_config: XBTConfig | None = None - """ - XBT configuration. - - If None, no XBTs can be cast. - """ - - model_config = pydantic.ConfigDict(extra="forbid") - - def to_yaml(self, file_path: str | Path) -> None: - """ - Write config to yaml file. - - :param file_path: Path to the file to write to. - """ - with open(file_path, "w") as file: - yaml.dump(self.model_dump(by_alias=True), file) - - @classmethod - def from_yaml(cls, file_path: str | Path) -> ShipConfig: - """ - Load config from yaml file. - - :param file_path: Path to the file to load from. - :returns: The config. - """ - with open(file_path) as file: - data = yaml.safe_load(file) - return ShipConfig(**data) - - def verify(self, schedule: Schedule) -> None: - """ - Verify the ship configuration against the provided schedule. - - This function performs two main tasks: - 1. Removes instrument configurations that are not present in the schedule. - 2. Verifies that all instruments in the schedule have corresponding configurations. - - Parameters - ---------- - schedule : Schedule - The schedule object containing the planned instruments and waypoints. - - Returns - ------- - None - - Raises - ------ - ConfigError - If an instrument in the schedule does not have a corresponding configuration. - - Notes - ----- - - Prints a message if a configuration is provided for an instrument not in the schedule. - - Sets the configuration to None for instruments not in the schedule. - - Raises a ConfigError for each instrument in the schedule that lacks a configuration. - - """ - instruments_in_schedule = schedule.get_instruments() - - for instrument in [ - "ARGO_FLOAT", - "DRIFTER", - "XBT", - "CTD", - "CTD_BGC", - ]: # TODO make instrument names consistent capitals or lowercase throughout codebase - if hasattr(self, instrument.lower() + "_config") and not any( - instrument == schedule_instrument.name - for schedule_instrument in instruments_in_schedule - ): - print(f"{instrument} configuration provided but not in schedule.") - setattr(self, instrument.lower() + "_config", None) - - # verify instruments in schedule have configuration - # TODO: the ConfigError message could be improved to explain that the **schedule** file has X instrument but the **ship_config** file does not - for instrument in instruments_in_schedule: - try: - InstrumentType(instrument) - except ValueError as e: - raise NotImplementedError("Instrument not supported.") from e - - if instrument == InstrumentType.ARGO_FLOAT and ( - not hasattr(self, "argo_float_config") or self.argo_float_config is None - ): - raise ConfigError( - "Planning has a waypoint with Argo float instrument, but configuration does not configure Argo floats." - ) - if instrument == InstrumentType.CTD and ( - not hasattr(self, "ctd_config") or self.ctd_config is None - ): - raise ConfigError( - "Planning has a waypoint with CTD instrument, but configuration does not configure CTDs." - ) - if instrument == InstrumentType.CTD_BGC and ( - not hasattr(self, "ctd_bgc_config") or self.ctd_bgc_config is None - ): - raise ConfigError( - "Planning has a waypoint with CTD_BGC instrument, but configuration does not configure CTD_BGCs." - ) - if instrument == InstrumentType.DRIFTER and ( - not hasattr(self, "drifter_config") or self.drifter_config is None - ): - raise ConfigError( - "Planning has a waypoint with drifter instrument, but configuration does not configure drifters." - ) - - if instrument == InstrumentType.XBT and ( - not hasattr(self, "xbt_config") or self.xbt_config is None - ): - raise ConfigError( - "Planning has a waypoint with XBT instrument, but configuration does not configure XBT." - ) From 43c855d9536ef648ce07fa86d22077aad1870fe1 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:23:59 +0200 Subject: [PATCH 15/56] add is_underway property to InstrumentType and filter instruments in plan UI --- src/virtualship/cli/_plan.py | 6 +++--- src/virtualship/instruments/master.py | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index 1aa3208b..435d5ab0 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -29,6 +29,7 @@ type_to_textual, ) from virtualship.errors import UnexpectedError, UserError +from virtualship.instruments.master import InstrumentType from virtualship.models import ( ADCPConfig, ArgoFloatConfig, @@ -36,7 +37,6 @@ CTDConfig, DrifterConfig, Expedition, - InstrumentType, Location, ShipConfig, ShipUnderwaterSTConfig, @@ -630,7 +630,7 @@ def _update_schedule(self): 0, ) wp.instrument = [] - for instrument in InstrumentType: + for instrument in [inst for inst in InstrumentType if not inst.is_underway]: switch_on = self.query_one(f"#wp{i}_{instrument.value}").value if instrument.value == "DRIFTER" and switch_on: count_str = self.query_one(f"#wp{i}_drifter_count").value @@ -901,7 +901,7 @@ def compose(self) -> ComposeResult: ) yield Label("Instruments:") - for instrument in InstrumentType: + for instrument in [i for i in InstrumentType if not i.is_underway]: is_selected = instrument in (self.waypoint.instrument or []) with Horizontal(): yield Label(instrument.value) diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index cb31468f..6e098988 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -14,6 +14,11 @@ class InstrumentType(Enum): ADCP = "ADCP" UNDERWATER_ST = "UNDERWATER_ST" + @property + def is_underway(self) -> bool: + """Return True if instrument is an underway instrument (ADCP, UNDERWATER_ST).""" + return self in {InstrumentType.ADCP, InstrumentType.UNDERWATER_ST} + def get_instruments_registry(): # local imports to avoid circular import issues From 79c81cb69ea8e25c669f8b4e9a58f16e95572504 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:24:59 +0200 Subject: [PATCH 16/56] enhance CLI output for fetching --- src/virtualship/cli/_fetch.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index c460db37..1cf33fa9 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -87,7 +87,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None ) shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) - # TODO: enhance CLI output for users? + click.echo(f"\n\n{(' Fetching data for: Bathymetry ').center(80, '=')}\n\n") # bathymetry (for all expeditions) copernicusmarine.subset( @@ -116,11 +116,14 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None # iterate across instruments and download data based on space_time_region for itype, instrument in filter_instruments.items(): + click.echo( + f"\n\n{(' Fetching data for: ' + itype.value + ' ').center(80, '=')}\n\n" + ) try: input_dataset = instrument["input_class"]( data_dir=download_folder, credentials=credentials, - space_time_region=expedition.space_time_region, + space_time_region=space_time_region, ) input_dataset.download_data() From efb53cada0ac63e87f8018aa1e8c0353a41039f7 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:25:52 +0200 Subject: [PATCH 17/56] general fixes and new error class --- src/virtualship/errors.py | 8 +++++++- src/virtualship/expedition/checkpoint.py | 3 ++- src/virtualship/expedition/do_expedition.py | 2 +- src/virtualship/expedition/simulate_schedule.py | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/virtualship/errors.py b/src/virtualship/errors.py index cdd58349..3ba52a9a 100644 --- a/src/virtualship/errors.py +++ b/src/virtualship/errors.py @@ -22,7 +22,7 @@ class ScheduleError(RuntimeError): pass -class ConfigError(RuntimeError): +class InstrumentsConfigError(RuntimeError): """An error in the config.""" pass @@ -38,3 +38,9 @@ class UnexpectedError(Exception): """Error raised when there is an unexpected problem.""" pass + + +class UnderwayConfigsError(Exception): + """Error raised when underway instrument configurations (ADCP or underwater ST) are missing.""" + + pass diff --git a/src/virtualship/expedition/checkpoint.py b/src/virtualship/expedition/checkpoint.py index 6daf1a9b..ff2dadd6 100644 --- a/src/virtualship/expedition/checkpoint.py +++ b/src/virtualship/expedition/checkpoint.py @@ -8,7 +8,8 @@ import yaml from virtualship.errors import CheckpointError -from virtualship.models import InstrumentType, Schedule +from virtualship.instruments.master import InstrumentType +from virtualship.models import Schedule class _YamlDumper(yaml.SafeDumper): diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index 5c46d2eb..921ea528 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -40,7 +40,7 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> expedition = _get_expedition(expedition_dir) # Verify instruments_config file is consistent with schedule - expedition.instruments_config.verify(expedition.schedule) + expedition.instruments_config.verify(expedition) # load last checkpoint checkpoint = _load_checkpoint(expedition_dir) diff --git a/src/virtualship/expedition/simulate_schedule.py b/src/virtualship/expedition/simulate_schedule.py index 3b78c5c7..784e2d32 100644 --- a/src/virtualship/expedition/simulate_schedule.py +++ b/src/virtualship/expedition/simulate_schedule.py @@ -11,10 +11,10 @@ from virtualship.instruments.ctd import CTD from virtualship.instruments.ctd_bgc import CTD_BGC from virtualship.instruments.drifter import Drifter +from virtualship.instruments.master import InstrumentType from virtualship.instruments.xbt import XBT from virtualship.models import ( Expedition, - InstrumentType, Location, Spacetime, Waypoint, From c4ddea1aeadef5ecba43abfe812eae744aa8518a Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:26:16 +0200 Subject: [PATCH 18/56] refactor test cases to use Expedition object --- tests/expedition/test_expedition.py | 140 +++++++++++++++++----------- 1 file changed, 85 insertions(+), 55 deletions(-) diff --git a/tests/expedition/test_expedition.py b/tests/expedition/test_expedition.py index a4643e03..4bed35e7 100644 --- a/tests/expedition/test_expedition.py +++ b/tests/expedition/test_expedition.py @@ -4,9 +4,14 @@ import pyproj import pytest -from virtualship.errors import ConfigError, ScheduleError +from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.expedition.do_expedition import _load_input_data -from virtualship.models import Expedition, Location, Schedule, Waypoint +from virtualship.models import ( + Expedition, + Location, + Schedule, + Waypoint, +) from virtualship.utils import EXPEDITION, _get_expedition, get_example_expedition projection = pyproj.Geod(ellps="WGS84") @@ -56,6 +61,7 @@ def test_verify_schedule() -> None: def test_get_instruments() -> None: + get_expedition = _get_expedition(expedition_dir) schedule = Schedule( waypoints=[ Waypoint(location=Location(0, 0), instrument=["CTD"]), @@ -63,12 +69,21 @@ def test_get_instruments() -> None: Waypoint(location=Location(1, 0), instrument=["CTD"]), ] ) - - assert set(instrument.name for instrument in schedule.get_instruments()) == { - "CTD", - "XBT", - "ARGO_FLOAT", - } + expedition = Expedition( + schedule=schedule, + instruments_config=get_expedition.instruments_config, + ship_config=get_expedition.ship_config, + ) + assert ( + set(instrument.name for instrument in expedition.get_instruments()) + == { + "CTD", + "UNDERWATER_ST", # not added above but underway instruments are auto present from instruments_config in expedition_dir/expedition.yaml + "ADCP", # as above + "ARGO_FLOAT", + "XBT", + } + ) @pytest.mark.parametrize( @@ -165,15 +180,15 @@ def test_verify_schedule_errors( @pytest.fixture -def schedule(tmp_file): +def expedition(tmp_file): with open(tmp_file, "w") as file: file.write(get_example_expedition()) - return Expedition.from_yaml(tmp_file).schedule + return Expedition.from_yaml(tmp_file) @pytest.fixture -def schedule_no_xbt(schedule): - for waypoint in schedule.waypoints: +def expedition_no_xbt(expedition): + for waypoint in expedition.schedule.waypoints: if waypoint.instrument and any( instrument.name == "XBT" for instrument in waypoint.instrument ): @@ -183,54 +198,57 @@ def schedule_no_xbt(schedule): if instrument.name != "XBT" ] - return schedule + return expedition @pytest.fixture -def instruments_config(tmp_file): - with open(tmp_file, "w") as file: - file.write(get_example_expedition()) - return Expedition.from_yaml(tmp_file).instruments_config +def instruments_config_no_xbt(expedition): + delattr(expedition.instruments_config, "xbt_config") + return expedition.instruments_config @pytest.fixture -def instruments_config_no_xbt(instruments_config): - delattr(instruments_config, "xbt_config") - return instruments_config +def instruments_config_no_ctd(expedition): + delattr(expedition.instruments_config, "ctd_config") + return expedition.instruments_config @pytest.fixture -def instruments_config_no_ctd(instruments_config): - delattr(instruments_config, "ctd_config") - return instruments_config +def instruments_config_no_ctd_bgc(expedition): + delattr(expedition.instruments_config, "ctd_bgc_config") + return expedition.instruments_config @pytest.fixture -def instruments_config_no_ctd_bgc(instruments_config): - delattr(instruments_config, "ctd_bgc_config") - return instruments_config +def instruments_config_no_argo_float(expedition): + delattr(expedition.instruments_config, "argo_float_config") + return expedition.instruments_config @pytest.fixture -def instruments_config_no_argo_float(instruments_config): - delattr(instruments_config, "argo_float_config") - return instruments_config +def instruments_config_no_drifter(expedition): + delattr(expedition.instruments_config, "drifter_config") + return expedition.instruments_config @pytest.fixture -def instruments_config_no_drifter(instruments_config): - delattr(instruments_config, "drifter_config") - return instruments_config +def instruments_config_no_adcp(expedition): + delattr(expedition.instruments_config, "adcp_config") + return expedition.instruments_config -def test_verify_instruments_config(instruments_config, schedule) -> None: - instruments_config.verify(schedule) +@pytest.fixture +def instruments_config_no_underwater_st(expedition): + delattr(expedition.instruments_config, "ship_underwater_st_config") + return expedition.instruments_config -def test_verify_instruments_config_no_instrument( - instruments_config, schedule_no_xbt -) -> None: - instruments_config.verify(schedule_no_xbt) +def test_verify_instruments_config(expedition) -> None: + expedition.instruments_config.verify(expedition) + + +def test_verify_instruments_config_no_instrument(expedition, expedition_no_xbt) -> None: + expedition.instruments_config.verify(expedition_no_xbt) @pytest.mark.parametrize( @@ -238,40 +256,52 @@ def test_verify_instruments_config_no_instrument( [ pytest.param( "instruments_config_no_xbt", - ConfigError, - "Schedule includes instrument 'XBT', but instruments_config does not provide configuration for it.", - id="ShipConfigNoXBT", + InstrumentsConfigError, + "Expedition includes instrument 'XBT', but instruments_config does not provide configuration for it.", + id="InstrumentsConfigNoXBT", ), pytest.param( "instruments_config_no_ctd", - ConfigError, - "Schedule includes instrument 'CTD', but instruments_config does not provide configuration for it.", - id="ShipConfigNoCTD", + InstrumentsConfigError, + "Expedition includes instrument 'CTD', but instruments_config does not provide configuration for it.", + id="InstrumentsConfigNoCTD", ), pytest.param( "instruments_config_no_ctd_bgc", - ConfigError, - "Schedule includes instrument 'CTD_BGC', but instruments_config does not provide configuration for it.", - id="ShipConfigNoCTD_BGC", + InstrumentsConfigError, + "Expedition includes instrument 'CTD_BGC', but instruments_config does not provide configuration for it.", + id="InstrumentsConfigNoCTD_BGC", ), pytest.param( "instruments_config_no_argo_float", - ConfigError, - "Schedule includes instrument 'ARGO_FLOAT', but instruments_config does not provide configuration for it.", - id="ShipConfigNoARGO_FLOAT", + InstrumentsConfigError, + "Expedition includes instrument 'ARGO_FLOAT', but instruments_config does not provide configuration for it.", + id="InstrumentsConfigNoARGO_FLOAT", ), pytest.param( "instruments_config_no_drifter", - ConfigError, - "Schedule includes instrument 'DRIFTER', but instruments_config does not provide configuration for it.", - id="ShipConfigNoDRIFTER", + InstrumentsConfigError, + "Expedition includes instrument 'DRIFTER', but instruments_config does not provide configuration for it.", + id="InstrumentsConfigNoDRIFTER", + ), + pytest.param( + "instruments_config_no_adcp", + InstrumentsConfigError, + r"Underway instrument config attribute\(s\) are missing from YAML\. Must be Config object or None\.", + id="InstrumentsConfigNoADCP", + ), + pytest.param( + "instruments_config_no_underwater_st", + InstrumentsConfigError, + r"Underway instrument config attribute\(s\) are missing from YAML\. Must be Config object or None\.", + id="InstrumentsConfigNoUNDERWATER_ST", ), ], ) def test_verify_instruments_config_errors( - request, schedule, instruments_config_fixture, error, match + request, expedition, instruments_config_fixture, error, match ) -> None: instruments_config = request.getfixturevalue(instruments_config_fixture) with pytest.raises(error, match=match): - instruments_config.verify(schedule) + instruments_config.verify(expedition) From 66aa4a52347fe7762b007d132f70daa9efd40545 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:35:13 +0100 Subject: [PATCH 19/56] move instruments base classes out of models/ dir --- src/virtualship/instruments/adcp.py | 5 +- src/virtualship/instruments/ctd.py | 2 +- src/virtualship/instruments/master.py | 113 +++++++++++++++++++++++++ src/virtualship/models/instruments.py | 115 -------------------------- 4 files changed, 115 insertions(+), 120 deletions(-) delete mode 100644 src/virtualship/models/instruments.py diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index ccf48738..cf5af169 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -3,14 +3,11 @@ from typing import ClassVar import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable +from parcels import FieldSet, ParticleSet, ScipyParticle, Variable from virtualship.models.instruments import InputDataset from virtualship.models.spacetime import Spacetime -## TODO: __init__.py will also need updating! -# + therefore instructions for adding new instruments will also involve adding to __init__.py as well as the new instrument script + update InstrumentType in instruments.py - @dataclass class ADCP: diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 15a46234..73125838 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -4,8 +4,8 @@ from typing import ClassVar import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable +from parcels import FieldSet, JITParticle, ParticleSet, Variable from virtualship.models.instruments import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 6e098988..43fe6a00 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -1,4 +1,14 @@ +import abc +from collections.abc import Callable +from datetime import timedelta from enum import Enum +from pathlib import Path + +import copernicusmarine +from yaspin import yaspin + +from virtualship.models.space_time_region import SpaceTimeRegion +from virtualship.utils import ship_spinner class InstrumentType(Enum): @@ -47,3 +57,106 @@ def get_instruments_registry(): for inst in InstrumentType if _input_class_map.get(inst.value) is not None } + + +# Base classes + + +class InputDataset(abc.ABC): + """Base class for instrument input datasets.""" + + def __init__( + self, + name: str, + latlon_buffer: float, + datetime_buffer: float, + min_depth: float, + max_depth: float, + data_dir: str, + credentials: dict, + space_time_region: SpaceTimeRegion, + ): + """Initialise input dataset.""" + self.name = name + self.latlon_buffer = latlon_buffer + self.datetime_buffer = datetime_buffer + self.min_depth = min_depth + self.max_depth = max_depth + self.data_dir = data_dir + self.credentials = credentials + self.space_time_region = space_time_region + + @abc.abstractmethod + def get_datasets_dict(self) -> dict: + """Get parameters for instrument's variable(s) specific data download.""" + ... + + def download_data(self) -> None: + """Download data for the instrument using copernicusmarine.""" + parameter_args = dict( + minimum_longitude=self.space_time_region.spatial_range.minimum_longitude + - self.latlon_buffer, + maximum_longitude=self.space_time_region.spatial_range.maximum_longitude + + self.latlon_buffer, + minimum_latitude=self.space_time_region.spatial_range.minimum_latitude + - self.latlon_buffer, + maximum_latitude=self.space_time_region.spatial_range.maximum_latitude + + self.latlon_buffer, + start_datetime=self.space_time_region.time_range.start_time, + end_datetime=self.space_time_region.time_range.end_time + + timedelta(days=self.datetime_buffer), + minimum_depth=abs(self.min_depth), + maximum_depth=abs(self.max_depth), + output_directory=self.data_dir, + username=self.credentials["username"], + password=self.credentials["password"], + overwrite=True, + coordinates_selection_method="outside", + ) + + datasets_args = self.get_datasets_dict() + + for dataset in datasets_args.values(): + download_args = {**parameter_args, **dataset} + copernicusmarine.subset(**download_args) + + +class Instrument(abc.ABC): + """Base class for instruments.""" + + def __init__( + self, + name: str, + config, + input_dataset: InputDataset, + kernels: list[Callable], + ): + """Initialise instrument.""" + self.name = name + self.config = config + self.input_data = input_dataset + self.kernels = kernels + + # def load_fieldset(self): + # """Load fieldset for simulation.""" + # # paths = self.input_data.get_fieldset_paths() + # ... + + def get_output_path(self, output_dir: Path) -> Path: + """Get output path for results.""" + return output_dir / f"{self.name}.zarr" + + def run(self): + """Run instrument simulation.""" + with yaspin( + text=f"Simulating {self.name} measurements... ", + side="right", + spinner=ship_spinner, + ) as spinner: + self.simulate() + spinner.ok("✅") + + @abc.abstractmethod + def simulate(self): + """Simulate instrument measurements.""" + ... diff --git a/src/virtualship/models/instruments.py b/src/virtualship/models/instruments.py deleted file mode 100644 index 72d9237d..00000000 --- a/src/virtualship/models/instruments.py +++ /dev/null @@ -1,115 +0,0 @@ -import abc -from collections.abc import Callable -from datetime import timedelta -from pathlib import Path - -import copernicusmarine -from yaspin import yaspin - -from virtualship.models.space_time_region import SpaceTimeRegion -from virtualship.utils import ship_spinner - -# TODO: -# Discussion: Should each instrument manage its own data files for modularity, -# or should we consolidate downloads to minimize file duplication across instruments? -# Consider starting with per-instrument files for simplicity, and refactor later if needed. - - -class InputDataset(abc.ABC): - """Base class for instrument input datasets.""" - - def __init__( - self, - name: str, - latlon_buffer: float, - datetime_buffer: float, - min_depth: float, - max_depth: float, - data_dir: str, - credentials: dict, - space_time_region: SpaceTimeRegion, - ): - """Initialise input dataset.""" - self.name = name - self.latlon_buffer = latlon_buffer - self.datetime_buffer = datetime_buffer - self.min_depth = min_depth - self.max_depth = max_depth - self.data_dir = data_dir - self.credentials = credentials - self.space_time_region = space_time_region - - @abc.abstractmethod - def get_datasets_dict(self) -> dict: - """Get parameters for instrument's variable(s) specific data download.""" - ... - - def download_data(self) -> None: - """Download data for the instrument using copernicusmarine.""" - parameter_args = dict( - minimum_longitude=self.space_time_region.spatial_range.minimum_longitude - - self.latlon_buffer, - maximum_longitude=self.space_time_region.spatial_range.maximum_longitude - + self.latlon_buffer, - minimum_latitude=self.space_time_region.spatial_range.minimum_latitude - - self.latlon_buffer, - maximum_latitude=self.space_time_region.spatial_range.maximum_latitude - + self.latlon_buffer, - start_datetime=self.space_time_region.time_range.start_time, - end_datetime=self.space_time_region.time_range.end_time - + timedelta(days=self.datetime_buffer), - minimum_depth=abs(self.min_depth), - maximum_depth=abs(self.max_depth), - output_directory=self.data_dir, - username=self.credentials["username"], - password=self.credentials["password"], - overwrite=True, - coordinates_selection_method="outside", - ) - - datasets_args = self.get_datasets_dict() - - for dataset in datasets_args.values(): - download_args = {**parameter_args, **dataset} - copernicusmarine.subset(**download_args) - - -class Instrument(abc.ABC): - """Base class for instruments.""" - - def __init__( - self, - name: str, - config, - input_dataset: InputDataset, - kernels: list[Callable], - ): - """Initialise instrument.""" - self.name = name - self.config = config - self.input_data = input_dataset - self.kernels = kernels - - # def load_fieldset(self): - # """Load fieldset for simulation.""" - # # paths = self.input_data.get_fieldset_paths() - # ... - - def get_output_path(self, output_dir: Path) -> Path: - """Get output path for results.""" - return output_dir / f"{self.name}.zarr" - - def run(self): - """Run instrument simulation.""" - with yaspin( - text=f"Simulating {self.name} measurements... ", - side="right", - spinner=ship_spinner, - ) as spinner: - self.simulate() - spinner.ok("✅") - - @abc.abstractmethod - def simulate(self): - """Simulate instrument measurements.""" - ... From 06ddf37e636775bfc01fa9201d749310b50c96e4 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:35:53 +0100 Subject: [PATCH 20/56] update base class imports --- src/virtualship/instruments/adcp.py | 2 +- src/virtualship/instruments/argo_float.py | 4 +- src/virtualship/instruments/ctd.py | 2 +- src/virtualship/instruments/ctd_bgc.py | 2 +- src/virtualship/instruments/drifter.py | 4 +- .../instruments/ship_underwater_st.py | 4 +- src/virtualship/instruments/xbt.py | 4 +- src/virtualship/models/expedition.py | 12 ++- tests/models/test_instruments.py | 97 ------------------- 9 files changed, 20 insertions(+), 111 deletions(-) delete mode 100644 tests/models/test_instruments.py diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index cf5af169..b80bb544 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -5,7 +5,7 @@ import numpy as np from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.models.instruments import InputDataset +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index b9949fb1..e9cadb8d 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -5,6 +5,7 @@ from typing import ClassVar import numpy as np + from parcels import ( AdvectionRK4, FieldSet, @@ -13,8 +14,7 @@ StatusCode, Variable, ) - -from virtualship.models.instruments import InputDataset +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 73125838..6a27d175 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -6,7 +6,7 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models.instruments import InputDataset +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 96c0efa7..74b5b81c 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -6,7 +6,7 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models.instruments import InputDataset +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 60ba5558..40aad9d4 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -4,9 +4,9 @@ from typing import ClassVar import numpy as np -from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models.instruments import InputDataset +from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 332c0b2c..3c7bc0ee 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -3,9 +3,9 @@ from typing import ClassVar import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.models.instruments import InputDataset +from parcels import FieldSet, ParticleSet, ScipyParticle, Variable +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 81cba7b9..4351d08e 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -4,9 +4,9 @@ from typing import ClassVar import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.models.instruments import InputDataset +from parcels import FieldSet, JITParticle, ParticleSet, Variable +from virtualship.instruments.master import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 7e206e8f..1b501119 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -9,7 +9,6 @@ import yaml from virtualship.errors import InstrumentsConfigError, ScheduleError -from virtualship.instruments.master import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta from .location import Location @@ -17,8 +16,8 @@ if TYPE_CHECKING: from parcels import FieldSet - from virtualship.expedition.input_data import InputData + from virtualship.instruments.master import InstrumentType projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") @@ -45,8 +44,10 @@ def from_yaml(cls, file_path: str) -> Expedition: data = yaml.safe_load(file) return Expedition(**data) - def get_instruments(self) -> set[InstrumentType]: + def get_instruments(self): """Return a set of unique InstrumentType enums used in the expedition.""" + from virtualship.instruments.master import InstrumentType + instruments_in_expedition = [] # from waypoints for waypoint in self.schedule.waypoints: @@ -114,6 +115,8 @@ def verify( """ print("\nVerifying route... ") + from virtualship.instruments.master import InstrumentType + if check_space_time_region and self.space_time_region is None: raise ScheduleError( "space_time_region not found in schedule, please define it to fetch the data." @@ -180,6 +183,7 @@ def verify( # check that ship will arrive on time at each waypoint (in case no unexpected event happen) time = self.waypoints[0].time + for wp_i, (wp, wp_next) in enumerate( zip(self.waypoints, self.waypoints[1:], strict=False) ): @@ -414,6 +418,8 @@ def verify(self, expedition: Expedition) -> None: Removes instrument configs not present in the schedule and checks that all scheduled instruments are configured. Raises ConfigError if any scheduled instrument is missing a config. """ + from virtualship.instruments.master import InstrumentType + instruments_in_expedition = expedition.get_instruments() instrument_config_map = { InstrumentType.ARGO_FLOAT: "argo_float_config", diff --git a/tests/models/test_instruments.py b/tests/models/test_instruments.py deleted file mode 100644 index 2ad73cdf..00000000 --- a/tests/models/test_instruments.py +++ /dev/null @@ -1,97 +0,0 @@ -import datetime -from unittest.mock import patch - -import pytest - -from virtualship.models.instruments import InputDataset -from virtualship.models.space_time_region import ( - SpaceTimeRegion, - SpatialRange, - TimeRange, -) - - -class DummyInputDataset(InputDataset): - """A minimal InputDataset subclass for testing purposes.""" - - def get_datasets_dict(self): - """Return a dummy datasets dict for testing.""" - return { - "dummy": { - "dataset_id": "test_id", - "variables": ["var1"], - "output_filename": "dummy.nc", - } - } - - -@pytest.fixture -def dummy_space_time_region(): - spatial_range = SpatialRange( - minimum_longitude=0, - maximum_longitude=1, - minimum_latitude=0, - maximum_latitude=1, - minimum_depth=0, - maximum_depth=10, - ) - base_time = datetime.datetime.strptime("1950-01-01", "%Y-%m-%d") - time_range = TimeRange( - start_time=base_time, - end_time=base_time + datetime.timedelta(hours=1), - ) - return SpaceTimeRegion( - spatial_range=spatial_range, - time_range=time_range, - ) - - -def test_inputdataset_abstract_instantiation(): - # instantiation should not be allowed - with pytest.raises(TypeError): - InputDataset( - name="test", - latlon_buffer=0, - datetime_buffer=0, - min_depth=0, - max_depth=10, - data_dir=".", - credentials={"username": "u", "password": "p"}, - space_time_region=None, - ) - - -def test_dummyinputdataset_initialization(dummy_space_time_region): - ds = DummyInputDataset( - name="test", - latlon_buffer=0.5, - datetime_buffer=1, - min_depth=0, - max_depth=10, - data_dir=".", - credentials={"username": "u", "password": "p"}, - space_time_region=dummy_space_time_region, - ) - assert ds.name == "test" - assert ds.latlon_buffer == 0.5 - assert ds.datetime_buffer == 1 - assert ds.min_depth == 0 - assert ds.max_depth == 10 - assert ds.data_dir == "." - assert ds.credentials["username"] == "u" - - -@patch("virtualship.models.instruments.copernicusmarine.subset") -def test_download_data_calls_subset(mock_subset, dummy_space_time_region): - ds = DummyInputDataset( - name="test", - latlon_buffer=0.5, - datetime_buffer=1, - min_depth=0, - max_depth=10, - data_dir=".", - credentials={"username": "u", "password": "p"}, - space_time_region=dummy_space_time_region, - ) - ds.download_data() - assert mock_subset.called From 5887177fd9df2e0387a2a5e32d7ede4457196a51 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:36:20 +0100 Subject: [PATCH 21/56] make get_instruments_registry more robust with testing --- src/virtualship/instruments/master.py | 4 +- tests/instruments/test_master.py | 109 ++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/instruments/test_master.py diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 43fe6a00..2139e8d5 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -55,12 +55,14 @@ def get_instruments_registry(): "input_class": _input_class_map.get(inst.value), } for inst in InstrumentType - if _input_class_map.get(inst.value) is not None } # Base classes +# TODO: could InputDataset and Instrument be unified? +# TODO: and all associated child classes... + class InputDataset(abc.ABC): """Base class for instrument input datasets.""" diff --git a/tests/instruments/test_master.py b/tests/instruments/test_master.py new file mode 100644 index 00000000..f84238d0 --- /dev/null +++ b/tests/instruments/test_master.py @@ -0,0 +1,109 @@ +import datetime +from unittest.mock import patch + +import pytest + +from virtualship.instruments.master import ( + InputDataset, + InstrumentType, + get_instruments_registry, +) +from virtualship.models.space_time_region import ( + SpaceTimeRegion, + SpatialRange, + TimeRange, +) + + +class DummyInputDataset(InputDataset): + """A minimal InputDataset subclass for testing purposes.""" + + def get_datasets_dict(self): + """Return a dummy datasets dict for testing.""" + return { + "dummy": { + "dataset_id": "test_id", + "variables": ["var1"], + "output_filename": "dummy.nc", + } + } + + +@pytest.fixture +def dummy_space_time_region(): + spatial_range = SpatialRange( + minimum_longitude=0, + maximum_longitude=1, + minimum_latitude=0, + maximum_latitude=1, + minimum_depth=0, + maximum_depth=10, + ) + base_time = datetime.datetime.strptime("1950-01-01", "%Y-%m-%d") + time_range = TimeRange( + start_time=base_time, + end_time=base_time + datetime.timedelta(hours=1), + ) + return SpaceTimeRegion( + spatial_range=spatial_range, + time_range=time_range, + ) + + +def test_inputdataset_abstract_instantiation(): + # instantiation should not be allowed + with pytest.raises(TypeError): + InputDataset( + name="test", + latlon_buffer=0, + datetime_buffer=0, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=None, + ) + + +def test_dummyinputdataset_initialization(dummy_space_time_region): + ds = DummyInputDataset( + name="test", + latlon_buffer=0.5, + datetime_buffer=1, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=dummy_space_time_region, + ) + assert ds.name == "test" + assert ds.latlon_buffer == 0.5 + assert ds.datetime_buffer == 1 + assert ds.min_depth == 0 + assert ds.max_depth == 10 + assert ds.data_dir == "." + assert ds.credentials["username"] == "u" + + +@patch("virtualship.models.instruments.copernicusmarine.subset") +def test_download_data_calls_subset(mock_subset, dummy_space_time_region): + ds = DummyInputDataset( + name="test", + latlon_buffer=0.5, + datetime_buffer=1, + min_depth=0, + max_depth=10, + data_dir=".", + credentials={"username": "u", "password": "p"}, + space_time_region=dummy_space_time_region, + ) + ds.download_data() + assert mock_subset.called + + +def test_all_instruments_have_input_class(): + registry = get_instruments_registry() + for instrument in InstrumentType: + entry = registry.get(instrument) + assert entry is not None, f"No registry entry for {instrument}" + assert entry.get("input_class") is not None, f"No input_class for {instrument}" From 8f5af0466679b8a284f2b76c39c1ecfb4c863854 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:41:55 +0100 Subject: [PATCH 22/56] refactor: reorganize instrument classes and update imports for clarity --- src/virtualship/cli/_fetch.py | 36 ++-- src/virtualship/cli/_plan.py | 2 +- src/virtualship/expedition/checkpoint.py | 2 +- .../expedition/simulate_schedule.py | 2 +- src/virtualship/instruments/adcp.py | 2 +- src/virtualship/instruments/argo_float.py | 2 +- src/virtualship/instruments/base.py | 109 ++++++++++++ src/virtualship/instruments/ctd.py | 2 +- src/virtualship/instruments/ctd_bgc.py | 2 +- src/virtualship/instruments/drifter.py | 2 +- src/virtualship/instruments/master.py | 164 ------------------ .../instruments/ship_underwater_st.py | 2 +- src/virtualship/instruments/types.py | 18 ++ src/virtualship/instruments/xbt.py | 2 +- src/virtualship/models/expedition.py | 11 +- 15 files changed, 163 insertions(+), 195 deletions(-) create mode 100644 src/virtualship/instruments/base.py create mode 100644 src/virtualship/instruments/types.py diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 140163a6..17fb403a 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -12,7 +12,6 @@ from pydantic import BaseModel from virtualship.errors import CopernicusCatalogueError, IncompleteDownloadError -from virtualship.instruments.master import get_instruments_registry from virtualship.utils import ( _dump_yaml, _generic_load_yaml, @@ -29,8 +28,6 @@ DOWNLOAD_METADATA = "download_metadata.yaml" -INSTRUMENTS = get_instruments_registry() - def _fetch(path: str | Path, username: str | None, password: str | None) -> None: """ @@ -110,29 +107,44 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None coordinates_selection_method="outside", ) - # access instrument classes but keep only instruments which are in schedule - filter_instruments = { - k: v for k, v in INSTRUMENTS.items() if k in instruments_in_expedition + # Direct mapping from InstrumentType to input dataset class + from virtualship.instruments.adcp import ADCPInputDataset + from virtualship.instruments.argo_float import ArgoFloatInputDataset + from virtualship.instruments.ctd import CTDInputDataset + from virtualship.instruments.ctd_bgc import CTD_BGCInputDataset + from virtualship.instruments.drifter import DrifterInputDataset + from virtualship.instruments.ship_underwater_st import Underwater_STInputDataset + from virtualship.instruments.types import InstrumentType + from virtualship.instruments.xbt import XBTInputDataset + + INSTRUMENT_INPUT_DATASET_MAP = { + InstrumentType.CTD: CTDInputDataset, + InstrumentType.CTD_BGC: CTD_BGCInputDataset, + InstrumentType.DRIFTER: DrifterInputDataset, + InstrumentType.ARGO_FLOAT: ArgoFloatInputDataset, + InstrumentType.XBT: XBTInputDataset, + InstrumentType.ADCP: ADCPInputDataset, + InstrumentType.UNDERWATER_ST: Underwater_STInputDataset, } - # iterate across instruments and download data based on space_time_region - for itype, instrument in filter_instruments.items(): + # Only keep instruments present in the expedition + for itype in instruments_in_expedition: + input_dataset_class = INSTRUMENT_INPUT_DATASET_MAP.get(itype) + if input_dataset_class is None: + continue click.echo( f"\n\n{(' Fetching data for: ' + itype.value + ' ').center(80, '=')}\n\n" ) try: - input_dataset = instrument["input_class"]( + input_dataset = input_dataset_class( data_dir=download_folder, credentials=credentials, space_time_region=space_time_region, ) - input_dataset.download_data() - except InvalidUsernameOrPassword as e: shutil.rmtree(download_folder) raise e - click.echo(f"{itype.value} data download completed.") complete_download(download_folder) diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index b6e44a70..a071c38e 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -29,7 +29,7 @@ type_to_textual, ) from virtualship.errors import UnexpectedError, UserError -from virtualship.instruments.master import InstrumentType +from virtualship.instruments.types import InstrumentType from virtualship.models import ( ADCPConfig, ArgoFloatConfig, diff --git a/src/virtualship/expedition/checkpoint.py b/src/virtualship/expedition/checkpoint.py index ff2dadd6..98fe1ae0 100644 --- a/src/virtualship/expedition/checkpoint.py +++ b/src/virtualship/expedition/checkpoint.py @@ -8,7 +8,7 @@ import yaml from virtualship.errors import CheckpointError -from virtualship.instruments.master import InstrumentType +from virtualship.instruments.types import InstrumentType from virtualship.models import Schedule diff --git a/src/virtualship/expedition/simulate_schedule.py b/src/virtualship/expedition/simulate_schedule.py index 784e2d32..f8d142ea 100644 --- a/src/virtualship/expedition/simulate_schedule.py +++ b/src/virtualship/expedition/simulate_schedule.py @@ -11,7 +11,7 @@ from virtualship.instruments.ctd import CTD from virtualship.instruments.ctd_bgc import CTD_BGC from virtualship.instruments.drifter import Drifter -from virtualship.instruments.master import InstrumentType +from virtualship.instruments.types import InstrumentType from virtualship.instruments.xbt import XBT from virtualship.models import ( Expedition, diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index b80bb544..078effeb 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -5,7 +5,7 @@ import numpy as np from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index e9cadb8d..c654fd1e 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -14,7 +14,7 @@ StatusCode, Variable, ) -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py new file mode 100644 index 00000000..dfdb9c6a --- /dev/null +++ b/src/virtualship/instruments/base.py @@ -0,0 +1,109 @@ +import abc +from collections.abc import Callable +from datetime import timedelta +from pathlib import Path + +import copernicusmarine +import yaspin + +from virtualship.models import SpaceTimeRegion +from virtualship.utils import ship_spinner + + +class InputDataset(abc.ABC): + """Base class for instrument input datasets.""" + + def __init__( + self, + name: str, + latlon_buffer: float, + datetime_buffer: float, + min_depth: float, + max_depth: float, + data_dir: str, + credentials: dict, + space_time_region: SpaceTimeRegion, + ): + """Initialise input dataset.""" + self.name = name + self.latlon_buffer = latlon_buffer + self.datetime_buffer = datetime_buffer + self.min_depth = min_depth + self.max_depth = max_depth + self.data_dir = data_dir + self.credentials = credentials + self.space_time_region = space_time_region + + @abc.abstractmethod + def get_datasets_dict(self) -> dict: + """Get parameters for instrument's variable(s) specific data download.""" + ... + + def download_data(self) -> None: + """Download data for the instrument using copernicusmarine.""" + parameter_args = dict( + minimum_longitude=self.space_time_region.spatial_range.minimum_longitude + - self.latlon_buffer, + maximum_longitude=self.space_time_region.spatial_range.maximum_longitude + + self.latlon_buffer, + minimum_latitude=self.space_time_region.spatial_range.minimum_latitude + - self.latlon_buffer, + maximum_latitude=self.space_time_region.spatial_range.maximum_latitude + + self.latlon_buffer, + start_datetime=self.space_time_region.time_range.start_time, + end_datetime=self.space_time_region.time_range.end_time + + timedelta(days=self.datetime_buffer), + minimum_depth=abs(self.min_depth), + maximum_depth=abs(self.max_depth), + output_directory=self.data_dir, + username=self.credentials["username"], + password=self.credentials["password"], + overwrite=True, + coordinates_selection_method="outside", + ) + + datasets_args = self.get_datasets_dict() + for dataset in datasets_args.values(): + download_args = {**parameter_args, **dataset} + copernicusmarine.subset(**download_args) + + +class Instrument(abc.ABC): + """Base class for instruments.""" + + def __init__( + self, + name: str, + config, + input_dataset: InputDataset, + kernels: list[Callable], + ): + """Initialise instrument.""" + self.name = name + self.config = config + self.input_data = input_dataset + self.kernels = kernels + + # def load_fieldset(self): + # """Load fieldset for simulation.""" + # # paths = self.input_data.get_fieldset_paths() + # ... + + def get_output_path(self, output_dir: Path) -> Path: + """Get output path for results.""" + return output_dir / f"{self.name}.zarr" + + def run(self): + """Run instrument simulation.""" + with yaspin( + text=f"Simulating {self.name} measurements... ", + side="right", + spinner=ship_spinner, + ) as spinner: + self.simulate() + spinner.ok("✅") + + @abc.abstractmethod + def simulate(self): + """Simulate instrument measurements.""" + ... diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 6a27d175..7d584601 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -6,7 +6,7 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 74b5b81c..1f45ce95 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -6,7 +6,7 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 40aad9d4..df765a0d 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -6,7 +6,7 @@ import numpy as np from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py index 2139e8d5..e69de29b 100644 --- a/src/virtualship/instruments/master.py +++ b/src/virtualship/instruments/master.py @@ -1,164 +0,0 @@ -import abc -from collections.abc import Callable -from datetime import timedelta -from enum import Enum -from pathlib import Path - -import copernicusmarine -from yaspin import yaspin - -from virtualship.models.space_time_region import SpaceTimeRegion -from virtualship.utils import ship_spinner - - -class InstrumentType(Enum): - """Types of the instruments.""" - - # TODO: scope for this to evaporate in the future...? - - CTD = "CTD" - CTD_BGC = "CTD_BGC" - DRIFTER = "DRIFTER" - ARGO_FLOAT = "ARGO_FLOAT" - XBT = "XBT" - ADCP = "ADCP" - UNDERWATER_ST = "UNDERWATER_ST" - - @property - def is_underway(self) -> bool: - """Return True if instrument is an underway instrument (ADCP, UNDERWATER_ST).""" - return self in {InstrumentType.ADCP, InstrumentType.UNDERWATER_ST} - - -def get_instruments_registry(): - # local imports to avoid circular import issues - from virtualship.instruments.adcp import ADCPInputDataset - from virtualship.instruments.argo_float import ArgoFloatInputDataset - from virtualship.instruments.ctd import CTDInputDataset - from virtualship.instruments.ctd_bgc import CTD_BGCInputDataset - from virtualship.instruments.drifter import DrifterInputDataset - from virtualship.instruments.ship_underwater_st import Underwater_STInputDataset - from virtualship.instruments.xbt import XBTInputDataset - - _input_class_map = { - "CTD": CTDInputDataset, - "CTD_BGC": CTD_BGCInputDataset, - "DRIFTER": DrifterInputDataset, - "ARGO_FLOAT": ArgoFloatInputDataset, - "XBT": XBTInputDataset, - "ADCP": ADCPInputDataset, - "UNDERWATER_ST": Underwater_STInputDataset, - } - - return { - inst: { - "input_class": _input_class_map.get(inst.value), - } - for inst in InstrumentType - } - - -# Base classes - -# TODO: could InputDataset and Instrument be unified? -# TODO: and all associated child classes... - - -class InputDataset(abc.ABC): - """Base class for instrument input datasets.""" - - def __init__( - self, - name: str, - latlon_buffer: float, - datetime_buffer: float, - min_depth: float, - max_depth: float, - data_dir: str, - credentials: dict, - space_time_region: SpaceTimeRegion, - ): - """Initialise input dataset.""" - self.name = name - self.latlon_buffer = latlon_buffer - self.datetime_buffer = datetime_buffer - self.min_depth = min_depth - self.max_depth = max_depth - self.data_dir = data_dir - self.credentials = credentials - self.space_time_region = space_time_region - - @abc.abstractmethod - def get_datasets_dict(self) -> dict: - """Get parameters for instrument's variable(s) specific data download.""" - ... - - def download_data(self) -> None: - """Download data for the instrument using copernicusmarine.""" - parameter_args = dict( - minimum_longitude=self.space_time_region.spatial_range.minimum_longitude - - self.latlon_buffer, - maximum_longitude=self.space_time_region.spatial_range.maximum_longitude - + self.latlon_buffer, - minimum_latitude=self.space_time_region.spatial_range.minimum_latitude - - self.latlon_buffer, - maximum_latitude=self.space_time_region.spatial_range.maximum_latitude - + self.latlon_buffer, - start_datetime=self.space_time_region.time_range.start_time, - end_datetime=self.space_time_region.time_range.end_time - + timedelta(days=self.datetime_buffer), - minimum_depth=abs(self.min_depth), - maximum_depth=abs(self.max_depth), - output_directory=self.data_dir, - username=self.credentials["username"], - password=self.credentials["password"], - overwrite=True, - coordinates_selection_method="outside", - ) - - datasets_args = self.get_datasets_dict() - - for dataset in datasets_args.values(): - download_args = {**parameter_args, **dataset} - copernicusmarine.subset(**download_args) - - -class Instrument(abc.ABC): - """Base class for instruments.""" - - def __init__( - self, - name: str, - config, - input_dataset: InputDataset, - kernels: list[Callable], - ): - """Initialise instrument.""" - self.name = name - self.config = config - self.input_data = input_dataset - self.kernels = kernels - - # def load_fieldset(self): - # """Load fieldset for simulation.""" - # # paths = self.input_data.get_fieldset_paths() - # ... - - def get_output_path(self, output_dir: Path) -> Path: - """Get output path for results.""" - return output_dir / f"{self.name}.zarr" - - def run(self): - """Run instrument simulation.""" - with yaspin( - text=f"Simulating {self.name} measurements... ", - side="right", - spinner=ship_spinner, - ) as spinner: - self.simulate() - spinner.ok("✅") - - @abc.abstractmethod - def simulate(self): - """Simulate instrument measurements.""" - ... diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 3c7bc0ee..fc4c1362 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -5,7 +5,7 @@ import numpy as np from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/types.py b/src/virtualship/instruments/types.py new file mode 100644 index 00000000..9ae221e9 --- /dev/null +++ b/src/virtualship/instruments/types.py @@ -0,0 +1,18 @@ +from enum import Enum + + +class InstrumentType(Enum): + """Types of the instruments.""" + + CTD = "CTD" + CTD_BGC = "CTD_BGC" + DRIFTER = "DRIFTER" + ARGO_FLOAT = "ARGO_FLOAT" + XBT = "XBT" + ADCP = "ADCP" + UNDERWATER_ST = "UNDERWATER_ST" + + @property + def is_underway(self) -> bool: + """Return True if instrument is an underway instrument (ADCP, UNDERWATER_ST).""" + return self in {InstrumentType.ADCP, InstrumentType.UNDERWATER_ST} diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 4351d08e..5d3b52ef 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -6,7 +6,7 @@ import numpy as np from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.master import InputDataset +from virtualship.instruments.base import InputDataset from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 1b501119..d7559fd0 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -9,6 +9,7 @@ import yaml from virtualship.errors import InstrumentsConfigError, ScheduleError +from virtualship.instruments.types import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta from .location import Location @@ -17,7 +18,6 @@ if TYPE_CHECKING: from parcels import FieldSet from virtualship.expedition.input_data import InputData - from virtualship.instruments.master import InstrumentType projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") @@ -44,10 +44,8 @@ def from_yaml(cls, file_path: str) -> Expedition: data = yaml.safe_load(file) return Expedition(**data) - def get_instruments(self): + def get_instruments(self) -> set[InstrumentType]: """Return a set of unique InstrumentType enums used in the expedition.""" - from virtualship.instruments.master import InstrumentType - instruments_in_expedition = [] # from waypoints for waypoint in self.schedule.waypoints: @@ -115,8 +113,6 @@ def verify( """ print("\nVerifying route... ") - from virtualship.instruments.master import InstrumentType - if check_space_time_region and self.space_time_region is None: raise ScheduleError( "space_time_region not found in schedule, please define it to fetch the data." @@ -183,7 +179,6 @@ def verify( # check that ship will arrive on time at each waypoint (in case no unexpected event happen) time = self.waypoints[0].time - for wp_i, (wp, wp_next) in enumerate( zip(self.waypoints, self.waypoints[1:], strict=False) ): @@ -418,8 +413,6 @@ def verify(self, expedition: Expedition) -> None: Removes instrument configs not present in the schedule and checks that all scheduled instruments are configured. Raises ConfigError if any scheduled instrument is missing a config. """ - from virtualship.instruments.master import InstrumentType - instruments_in_expedition = expedition.get_instruments() instrument_config_map = { InstrumentType.ARGO_FLOAT: "argo_float_config", From fdc0e6e4f23908cd954cf06f79dbbfd17261bcfe Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:18:14 +0100 Subject: [PATCH 23/56] implement instrument registration and input dataset retrieval --- src/virtualship/cli/_fetch.py | 23 ++----------------- src/virtualship/instruments/adcp.py | 3 +++ src/virtualship/instruments/argo_float.py | 3 +++ src/virtualship/instruments/ctd.py | 3 +++ src/virtualship/instruments/ctd_bgc.py | 3 +++ src/virtualship/instruments/drifter.py | 3 +++ src/virtualship/instruments/master.py | 0 .../instruments/ship_underwater_st.py | 3 +++ src/virtualship/instruments/xbt.py | 3 +++ src/virtualship/utils.py | 15 ++++++++++++ 10 files changed, 38 insertions(+), 21 deletions(-) delete mode 100644 src/virtualship/instruments/master.py diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 17fb403a..50dda3a5 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -16,6 +16,7 @@ _dump_yaml, _generic_load_yaml, _get_expedition, + get_input_dataset_class, ) if TYPE_CHECKING: @@ -107,29 +108,9 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None coordinates_selection_method="outside", ) - # Direct mapping from InstrumentType to input dataset class - from virtualship.instruments.adcp import ADCPInputDataset - from virtualship.instruments.argo_float import ArgoFloatInputDataset - from virtualship.instruments.ctd import CTDInputDataset - from virtualship.instruments.ctd_bgc import CTD_BGCInputDataset - from virtualship.instruments.drifter import DrifterInputDataset - from virtualship.instruments.ship_underwater_st import Underwater_STInputDataset - from virtualship.instruments.types import InstrumentType - from virtualship.instruments.xbt import XBTInputDataset - - INSTRUMENT_INPUT_DATASET_MAP = { - InstrumentType.CTD: CTDInputDataset, - InstrumentType.CTD_BGC: CTD_BGCInputDataset, - InstrumentType.DRIFTER: DrifterInputDataset, - InstrumentType.ARGO_FLOAT: ArgoFloatInputDataset, - InstrumentType.XBT: XBTInputDataset, - InstrumentType.ADCP: ADCPInputDataset, - InstrumentType.UNDERWATER_ST: Underwater_STInputDataset, - } - # Only keep instruments present in the expedition for itype in instruments_in_expedition: - input_dataset_class = INSTRUMENT_INPUT_DATASET_MAP.get(itype) + input_dataset_class = get_input_dataset_class(itype) if input_dataset_class is None: continue click.echo( diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 078effeb..1bc67e00 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -6,7 +6,9 @@ from parcels import FieldSet, ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -32,6 +34,7 @@ def _sample_velocity(particle, fieldset, time): ) +@register_instrument(InstrumentType.ADCP) class ADCPInputDataset(InputDataset): """Input dataset for ADCP instrument.""" diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index c654fd1e..7f7d23a1 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -15,7 +15,9 @@ Variable, ) from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -116,6 +118,7 @@ def _check_error(particle, fieldset, time): particle.delete() +@register_instrument(InstrumentType.ARGO_FLOAT) class ArgoFloatInputDataset(InputDataset): """Input dataset for ArgoFloat instrument.""" diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 7d584601..cd8fd330 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -7,7 +7,9 @@ from parcels import FieldSet, JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -54,6 +56,7 @@ def _ctd_cast(particle, fieldset, time): particle.delete() +@register_instrument(InstrumentType.CTD) class CTDInputDataset(InputDataset): """Input dataset for CTD instrument.""" diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 1f45ce95..92f717db 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -7,7 +7,9 @@ from parcels import FieldSet, JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -79,6 +81,7 @@ def _ctd_bgc_cast(particle, fieldset, time): particle.delete() +@register_instrument(InstrumentType.CTD_BGC) class CTD_BGCInputDataset(InputDataset): """Input dataset object for CTD_BGC instrument.""" diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index df765a0d..4ca0d087 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -7,7 +7,9 @@ from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -41,6 +43,7 @@ def _check_lifetime(particle, fieldset, time): particle.delete() +@register_instrument(InstrumentType.DRIFTER) class DrifterInputDataset(InputDataset): """Input dataset for Drifter instrument.""" diff --git a/src/virtualship/instruments/master.py b/src/virtualship/instruments/master.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index fc4c1362..accfb5b3 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -6,7 +6,9 @@ from parcels import FieldSet, ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -34,6 +36,7 @@ def _sample_temperature(particle, fieldset, time): particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] +@register_instrument(InstrumentType.UNDERWATER_ST) class Underwater_STInputDataset(InputDataset): """Input dataset for Underwater_ST instrument.""" diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 5d3b52ef..f5ec5fd0 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -7,7 +7,9 @@ from parcels import FieldSet, JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime +from virtualship.utils import register_instrument @dataclass @@ -55,6 +57,7 @@ def _xbt_cast(particle, fieldset, time): particle_ddepth = particle.max_depth - particle.depth +@register_instrument(InstrumentType.XBT) class XBTInputDataset(InputDataset): """Input dataset for XBT instrument.""" diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 0a39d035..e1054da1 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -239,3 +239,18 @@ def _get_expedition(expedition_dir: Path) -> Expedition: "🚢 ", ], ) + +# InstrumentType -> InputDataset registry and registration utilities. +INSTRUMENT_INPUT_DATASET_MAP = {} + + +def register_instrument(instrument_type): + def decorator(cls): + INSTRUMENT_INPUT_DATASET_MAP[instrument_type] = cls + return cls + + return decorator + + +def get_input_dataset_class(instrument_type): + return INSTRUMENT_INPUT_DATASET_MAP.get(instrument_type) From 95e6cbd0d47797f69dee2745094812e288666dd5 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:44:24 +0100 Subject: [PATCH 24/56] refactor: reorganize imports in instrument test files for consistency --- src/virtualship/instruments/__init__.py | 20 +++++++++++++++++++ .../{test_master.py => test_base.py} | 16 ++++++--------- tests/instruments/test_ctd.py | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) rename tests/instruments/{test_master.py => test_base.py} (86%) diff --git a/src/virtualship/instruments/__init__.py b/src/virtualship/instruments/__init__.py index 5324da2c..b593ed38 100644 --- a/src/virtualship/instruments/__init__.py +++ b/src/virtualship/instruments/__init__.py @@ -1 +1,21 @@ """Instruments in VirtualShip.""" + +from . import ( + adcp, + argo_float, + ctd, + ctd_bgc, + drifter, + ship_underwater_st, + xbt, +) + +__all__ = [ + "adcp", + "argo_float", + "ctd", + "ctd_bgc", + "drifter", + "ship_underwater_st", + "xbt", +] diff --git a/tests/instruments/test_master.py b/tests/instruments/test_base.py similarity index 86% rename from tests/instruments/test_master.py rename to tests/instruments/test_base.py index f84238d0..f4192092 100644 --- a/tests/instruments/test_master.py +++ b/tests/instruments/test_base.py @@ -3,16 +3,14 @@ import pytest -from virtualship.instruments.master import ( - InputDataset, - InstrumentType, - get_instruments_registry, -) +from virtualship.instruments.base import InputDataset +from virtualship.instruments.types import InstrumentType from virtualship.models.space_time_region import ( SpaceTimeRegion, SpatialRange, TimeRange, ) +from virtualship.utils import get_input_dataset_class class DummyInputDataset(InputDataset): @@ -85,7 +83,7 @@ def test_dummyinputdataset_initialization(dummy_space_time_region): assert ds.credentials["username"] == "u" -@patch("virtualship.models.instruments.copernicusmarine.subset") +@patch("virtualship.instruments.base.copernicusmarine.subset") def test_download_data_calls_subset(mock_subset, dummy_space_time_region): ds = DummyInputDataset( name="test", @@ -102,8 +100,6 @@ def test_download_data_calls_subset(mock_subset, dummy_space_time_region): def test_all_instruments_have_input_class(): - registry = get_instruments_registry() for instrument in InstrumentType: - entry = registry.get(instrument) - assert entry is not None, f"No registry entry for {instrument}" - assert entry.get("input_class") is not None, f"No input_class for {instrument}" + input_class = get_input_dataset_class(instrument) + assert input_class is not None, f"No input_class for {instrument}" diff --git a/tests/instruments/test_ctd.py b/tests/instruments/test_ctd.py index 14e0a276..0a8edcfa 100644 --- a/tests/instruments/test_ctd.py +++ b/tests/instruments/test_ctd.py @@ -9,8 +9,8 @@ import numpy as np import xarray as xr -from parcels import Field, FieldSet +from parcels import Field, FieldSet from virtualship.instruments.ctd import CTD, simulate_ctd from virtualship.models import Location, Spacetime From 01cee18ff6e52781ad9335cbbe4de512b265cca7 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:50:21 +0100 Subject: [PATCH 25/56] further refactoring: instrument classes to use a unified InputDataset and Instrument structure --- src/virtualship/cli/_fetch.py | 2 +- src/virtualship/expedition/do_expedition.py | 81 +++--- src/virtualship/expedition/input_data.py | 255 ------------------ src/virtualship/instruments/adcp.py | 138 +++++----- src/virtualship/instruments/argo_float.py | 177 ++++++------ src/virtualship/instruments/base.py | 94 +++++-- src/virtualship/instruments/ctd.py | 193 +++++++------ src/virtualship/instruments/ctd_bgc.py | 231 ++++++++-------- src/virtualship/instruments/drifter.py | 180 ++++++------- .../instruments/ship_underwater_st.py | 124 ++++----- src/virtualship/instruments/xbt.py | 197 +++++++------- src/virtualship/models/expedition.py | 25 +- src/virtualship/utils.py | 22 +- 13 files changed, 746 insertions(+), 973 deletions(-) delete mode 100644 src/virtualship/expedition/input_data.py diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 50dda3a5..67695695 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -112,7 +112,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None for itype in instruments_in_expedition: input_dataset_class = get_input_dataset_class(itype) if input_dataset_class is None: - continue + raise RuntimeError(f"No input dataset class found for type {itype}.") click.echo( f"\n\n{(' Fetching data for: ' + itype.value + ' ').center(80, '=')}\n\n" ) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index 921ea528..ef5b6037 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -6,17 +6,11 @@ import pyproj -from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.models import Expedition, Schedule -from virtualship.utils import ( - CHECKPOINT, - _get_expedition, -) +from virtualship.models import Schedule +from virtualship.utils import CHECKPOINT, _get_expedition, get_instrument_class from .checkpoint import Checkpoint from .expedition_cost import expedition_cost -from .input_data import InputData -from .simulate_measurements import simulate_measurements from .simulate_schedule import ScheduleProblem, simulate_schedule # projection used to sail between waypoints @@ -51,6 +45,7 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> checkpoint.verify(expedition.schedule) # load fieldsets + _load_input_data = [] # TEMPORARY! loaded_input_data = _load_input_data( expedition_dir=expedition_dir, expedition=expedition, @@ -106,56 +101,42 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> # simulate measurements print("\nSimulating measurements. This may take a while...\n") - simulate_measurements( - expedition_dir, - expedition.instruments_config, - loaded_input_data, - schedule_results.measurements_to_simulate, - ) - print("\nAll measurement simulations are complete.") - print("\n----- EXPEDITION RESULTS ------") - print("\nYour expedition has concluded successfully!") - print( - f"Your measurements can be found in the '{expedition_dir}/results' directory." - ) - print("\n------------- END -------------\n") + # TODO: this is where XYZInstrument.run() could be called instead of simulate_measurements!? + # TODO: this time maybe looping through measurements to simulate in some form... + # TODO: first in explicit per instrument, then think about whether can be automated more...not the end of the world if just have to explain in documentation that changes must be made here... + instruments_in_expedition = expedition.get_instruments() -def _load_input_data( - expedition_dir: Path, - expedition: Expedition, - input_data: Path | None, -) -> InputData: - """ - Load the input data. + for itype in instruments_in_expedition: + instrument_class = get_instrument_class(itype) + if instrument_class is None: + raise RuntimeError(f"No instrument class found for type {itype}.") - :param expedition_dir: Directory of the expedition. - :param expedition: Expedition object. - :param input_data: Folder containing input data. - :return: InputData object. - """ - if input_data is None: - space_time_region_hash = get_space_time_region_hash( - expedition.schedule.space_time_region + measurements = schedule_results.measurements_to_simulate.get(itype.name.lower()) + + instrument_class.run( + expedition_dir.joinpath("results", f"{itype.name.lower()}.zarr"), + measurements=measurements, + fieldset=loaded_input_data.get_fieldset_for_instrument_type(itype), + expedition=expedition, ) - input_data = get_existing_download(expedition_dir, space_time_region_hash) - assert input_data is not None, ( - "Input data hasn't been found. Have you run the `virtualship fetch` command?" - ) + # simulate_measurements( + # expedition_dir, + # expedition.instruments_config, + # loaded_input_data, + # schedule_results.measurements_to_simulate, + # ) - return InputData.load( - directory=input_data, - load_adcp=expedition.instruments_config.adcp_config is not None, - load_argo_float=expedition.instruments_config.argo_float_config is not None, - load_ctd=expedition.instruments_config.ctd_config is not None, - load_ctd_bgc=expedition.instruments_config.ctd_bgc_config is not None, - load_drifter=expedition.instruments_config.drifter_config is not None, - load_xbt=expedition.instruments_config.xbt_config is not None, - load_ship_underwater_st=expedition.instruments_config.ship_underwater_st_config - is not None, + print("\nAll measurement simulations are complete.") + + print("\n----- EXPEDITION RESULTS ------") + print("\nYour expedition has concluded successfully!") + print( + f"Your measurements can be found in the '{expedition_dir}/results' directory." ) + print("\n------------- END -------------\n") def _load_checkpoint(expedition_dir: Path) -> Checkpoint | None: diff --git a/src/virtualship/expedition/input_data.py b/src/virtualship/expedition/input_data.py deleted file mode 100644 index fa48e0a7..00000000 --- a/src/virtualship/expedition/input_data.py +++ /dev/null @@ -1,255 +0,0 @@ -"""InputData class.""" - -from __future__ import annotations - -from dataclasses import dataclass -from pathlib import Path - -from parcels import Field, FieldSet - - -@dataclass -class InputData: - """A collection of fieldsets that function as input data for simulation.""" - - adcp_fieldset: FieldSet | None - argo_float_fieldset: FieldSet | None - ctd_fieldset: FieldSet | None - ctd_bgc_fieldset: FieldSet | None - drifter_fieldset: FieldSet | None - xbt_fieldset: FieldSet | None - ship_underwater_st_fieldset: FieldSet | None - - @classmethod - def load( - cls, - directory: str | Path, - load_adcp: bool, - load_argo_float: bool, - load_ctd: bool, - load_ctd_bgc: bool, - load_drifter: bool, - load_xbt: bool, - load_ship_underwater_st: bool, - ) -> InputData: - """ - Create an instance of this class from netCDF files. - - For now this function makes a lot of assumption about file location and contents. - - :param directory: Input data directory. - :param load_adcp: Whether to load the ADCP fieldset. - :param load_argo_float: Whether to load the argo float fieldset. - :param load_ctd: Whether to load the CTD fieldset. - :param load_ctd_bgc: Whether to load the CTD BGC fieldset. - :param load_drifter: Whether to load the drifter fieldset. - :param load_ship_underwater_st: Whether to load the ship underwater ST fieldset. - :returns: An instance of this class with loaded fieldsets. - """ - directory = Path(directory) - if load_drifter: - drifter_fieldset = cls._load_drifter_fieldset(directory) - else: - drifter_fieldset = None - if load_argo_float: - argo_float_fieldset = cls._load_argo_float_fieldset(directory) - else: - argo_float_fieldset = None - if load_ctd_bgc: - ctd_bgc_fieldset = cls._load_ctd_bgc_fieldset(directory) - else: - ctd_bgc_fieldset = None - if load_adcp or load_ctd or load_ship_underwater_st or load_xbt: - ship_fieldset = cls._load_ship_fieldset(directory) - if load_adcp: - adcp_fieldset = ship_fieldset - else: - adcp_fieldset = None - if load_ctd: - ctd_fieldset = ship_fieldset - else: - ctd_fieldset = None - if load_ship_underwater_st: - ship_underwater_st_fieldset = ship_fieldset - else: - ship_underwater_st_fieldset = None - if load_xbt: - xbt_fieldset = ship_fieldset - else: - xbt_fieldset = None - - return InputData( - adcp_fieldset=adcp_fieldset, - argo_float_fieldset=argo_float_fieldset, - ctd_fieldset=ctd_fieldset, - ctd_bgc_fieldset=ctd_bgc_fieldset, - drifter_fieldset=drifter_fieldset, - xbt_fieldset=xbt_fieldset, - ship_underwater_st_fieldset=ship_underwater_st_fieldset, - ) - - @classmethod - def _load_ship_fieldset(cls, directory: Path) -> FieldSet: - filenames = { - "U": directory.joinpath("ship_uv.nc"), - "V": directory.joinpath("ship_uv.nc"), - "S": directory.joinpath("ship_s.nc"), - "T": directory.joinpath("ship_t.nc"), - } - variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} - dimensions = { - "lon": "longitude", - "lat": "latitude", - "time": "time", - "depth": "depth", - } - - # create the fieldset and set interpolation methods - fieldset = FieldSet.from_netcdf( - filenames, variables, dimensions, allow_time_extrapolation=True - ) - fieldset.T.interp_method = "linear_invdist_land_tracer" - fieldset.S.interp_method = "linear_invdist_land_tracer" - - # make depth negative - for g in fieldset.gridset.grids: - g.negate_depth() - - # add bathymetry data - bathymetry_file = directory.joinpath("bathymetry.nc") - bathymetry_variables = ("bathymetry", "deptho") - bathymetry_dimensions = {"lon": "longitude", "lat": "latitude"} - bathymetry_field = Field.from_netcdf( - bathymetry_file, bathymetry_variables, bathymetry_dimensions - ) - # make depth negative - bathymetry_field.data = -bathymetry_field.data - fieldset.add_field(bathymetry_field) - - # read in data already - fieldset.computeTimeChunk(0, 1) - - return fieldset - - @classmethod - def _load_ctd_bgc_fieldset(cls, directory: Path) -> FieldSet: - filenames = { - "U": directory.joinpath("ship_uv.nc"), - "V": directory.joinpath("ship_uv.nc"), - "o2": directory.joinpath("ctd_bgc_o2.nc"), - "chl": directory.joinpath("ctd_bgc_chl.nc"), - "no3": directory.joinpath("ctd_bgc_no3.nc"), - "po4": directory.joinpath("ctd_bgc_po4.nc"), - "ph": directory.joinpath("ctd_bgc_ph.nc"), - "phyc": directory.joinpath("ctd_bgc_phyc.nc"), - "nppv": directory.joinpath("ctd_bgc_nppv.nc"), - } - variables = { - "U": "uo", - "V": "vo", - "o2": "o2", - "chl": "chl", - "no3": "no3", - "po4": "po4", - "ph": "ph", - "phyc": "phyc", - "nppv": "nppv", - } - dimensions = { - "lon": "longitude", - "lat": "latitude", - "time": "time", - "depth": "depth", - } - - fieldset = FieldSet.from_netcdf( - filenames, variables, dimensions, allow_time_extrapolation=True - ) - fieldset.o2.interp_method = "linear_invdist_land_tracer" - fieldset.chl.interp_method = "linear_invdist_land_tracer" - fieldset.no3.interp_method = "linear_invdist_land_tracer" - fieldset.po4.interp_method = "linear_invdist_land_tracer" - fieldset.ph.interp_method = "linear_invdist_land_tracer" - fieldset.phyc.interp_method = "linear_invdist_land_tracer" - fieldset.nppv.interp_method = "linear_invdist_land_tracer" - - # make depth negative - for g in fieldset.gridset.grids: - g.negate_depth() - - # add bathymetry data - bathymetry_file = directory.joinpath("bathymetry.nc") - bathymetry_variables = ("bathymetry", "deptho") - bathymetry_dimensions = {"lon": "longitude", "lat": "latitude"} - bathymetry_field = Field.from_netcdf( - bathymetry_file, bathymetry_variables, bathymetry_dimensions - ) - # make depth negative - bathymetry_field.data = -bathymetry_field.data - fieldset.add_field(bathymetry_field) - - # read in data already - fieldset.computeTimeChunk(0, 1) - - return fieldset - - @classmethod - def _load_drifter_fieldset(cls, directory: Path) -> FieldSet: - filenames = { - "U": directory.joinpath("drifter_uv.nc"), - "V": directory.joinpath("drifter_uv.nc"), - "T": directory.joinpath("drifter_t.nc"), - } - variables = {"U": "uo", "V": "vo", "T": "thetao"} - dimensions = { - "lon": "longitude", - "lat": "latitude", - "time": "time", - "depth": "depth", - } - - fieldset = FieldSet.from_netcdf( - filenames, variables, dimensions, allow_time_extrapolation=False - ) - fieldset.T.interp_method = "linear_invdist_land_tracer" - - # make depth negative - for g in fieldset.gridset.grids: - g.negate_depth() - - # read in data already - fieldset.computeTimeChunk(0, 1) - - return fieldset - - @classmethod - def _load_argo_float_fieldset(cls, directory: Path) -> FieldSet: - filenames = { - "U": directory.joinpath("argo_float_uv.nc"), - "V": directory.joinpath("argo_float_uv.nc"), - "S": directory.joinpath("argo_float_s.nc"), - "T": directory.joinpath("argo_float_t.nc"), - } - variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} - dimensions = { - "lon": "longitude", - "lat": "latitude", - "time": "time", - "depth": "depth", - } - - fieldset = FieldSet.from_netcdf( - filenames, variables, dimensions, allow_time_extrapolation=False - ) - fieldset.T.interp_method = "linear_invdist_land_tracer" - fieldset.S.interp_method = "linear_invdist_land_tracer" - - # make depth negative - for g in fieldset.gridset.grids: - if max(g.depth) > 0: - g.negate_depth() - - # read in data already - fieldset.computeTimeChunk(0, 1) - - return fieldset diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 1bc67e00..5caab5bf 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -4,11 +4,11 @@ import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.instruments.base import InputDataset +from parcels import ParticleSet, ScipyParticle, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -34,7 +34,7 @@ def _sample_velocity(particle, fieldset, time): ) -@register_instrument(InstrumentType.ADCP) +@register_input_dataset(InstrumentType.ADCP) class ADCPInputDataset(InputDataset): """Input dataset for ADCP instrument.""" @@ -69,78 +69,64 @@ def get_datasets_dict(self) -> dict: } -# TODO: uncomment when ready for new simulation logic! -# class ADCPInstrument(instruments.Instrument): -# """ADCP instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(ADCP.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -# TODO: to be replaced with new simulation logic -## -- old simulation code - - -def simulate_adcp( - fieldset: FieldSet, - out_path: str | Path, - max_depth: float, - min_depth: float, - num_bins: int, - sample_points: list[Spacetime], -) -> None: - """ - Use Parcels to simulate an ADCP in a fieldset. - - :param fieldset: The fieldset to simulate the ADCP in. - :param out_path: The path to write the results to. - :param max_depth: Maximum depth the ADCP can measure. - :param min_depth: Minimum depth the ADCP can measure. - :param num_bins: How many samples to take in the complete range between max_depth and min_depth. - :param sample_points: The places and times to sample at. - """ - sample_points.sort(key=lambda p: p.time) - - bins = np.linspace(max_depth, min_depth, num_bins) - num_particles = len(bins) - particleset = ParticleSet.from_list( - fieldset=fieldset, - pclass=_ADCPParticle, - lon=np.full( - num_particles, 0.0 - ), # initial lat/lon are irrelevant and will be overruled later. - lat=np.full(num_particles, 0.0), - depth=bins, - time=0, # same for time - ) - - # define output file for the simulation - # outputdt set to infinite as we just want to write at the end of every call to 'execute' - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - - for point in sample_points: - particleset.lon_nextloop[:] = point.location.lon - particleset.lat_nextloop[:] = point.location.lat - particleset.time_nextloop[:] = fieldset.time_origin.reltime( - np.datetime64(point.time) +@register_instrument(InstrumentType.ADCP) +class ADCPInstrument(Instrument): + """ADCP instrument class.""" + + def __init__( + self, + input_dataset: InputDataset, + ): + """Initialize ADCPInstrument.""" + filenames = { + "UV": input_dataset.data_dir.joinpath(f"{input_dataset.name}_uv.nc"), + } + variables = {"UV": ["uo", "vo"]} + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=False, + allow_time_extrapolation=True, ) - # perform one step using the particleset - # dt and runtime are set so exactly one step is made. - particleset.execute( - [_sample_velocity], - dt=1, - runtime=1, - verbose_progress=False, - output_file=out_file, + def simulate( + self, + out_path: str | Path, + max_depth: float, + min_depth: float, + num_bins: int, + sample_points: list[Spacetime], + ) -> None: + """Simulate ADCP measurements.""" + sample_points.sort(key=lambda p: p.time) + + fieldset = self.load_input_data() + + bins = np.linspace(max_depth, min_depth, num_bins) + num_particles = len(bins) + particleset = ParticleSet.from_list( + fieldset=fieldset, + pclass=_ADCPParticle, + lon=np.full(num_particles, 0.0), + lat=np.full(num_particles, 0.0), + depth=bins, + time=0, ) + + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + + for point in sample_points: + particleset.lon_nextloop[:] = point.location.lon + particleset.lat_nextloop[:] = point.location.lat + particleset.time_nextloop[:] = fieldset.time_origin.reltime( + np.datetime64(point.time) + ) + + particleset.execute( + [_sample_velocity], + dt=1, + runtime=1, + verbose_progress=False, + output_file=out_file, + ) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 7f7d23a1..66b25bdf 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -8,16 +8,15 @@ from parcels import ( AdvectionRK4, - FieldSet, JITParticle, ParticleSet, StatusCode, Variable, ) -from virtualship.instruments.base import InputDataset +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -118,7 +117,7 @@ def _check_error(particle, fieldset, time): particle.delete() -@register_instrument(InstrumentType.ARGO_FLOAT) +@register_input_dataset(InstrumentType.ARGO_FLOAT) class ArgoFloatInputDataset(InputDataset): """Input dataset for ArgoFloat instrument.""" @@ -163,89 +162,89 @@ def get_datasets_dict(self) -> dict: } -# class ArgoFloatInstrument(instruments.Instrument): -# """ArgoFloat instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(ArgoFloat.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_argo_floats( - fieldset: FieldSet, - out_path: str | Path, - argo_floats: list[ArgoFloat], - outputdt: timedelta, - endtime: datetime | None, -) -> None: - """ - Use Parcels to simulate a set of Argo floats in a fieldset. - - :param fieldset: The fieldset to simulate the Argo floats in. - :param out_path: The path to write the results to. - :param argo_floats: A list of Argo floats to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :param endtime: Stop at this time, or if None, continue until the end of the fieldset. - """ - DT = 10.0 # dt of Argo float simulation integrator - - if len(argo_floats) == 0: - print( - "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." +@register_instrument(InstrumentType.ARGO_FLOAT) +class ArgoFloatInstrument(Instrument): + """ArgoFloat instrument class.""" + + def __init__( + self, + input_dataset: InputDataset, + ): + """Initialize ArgoFloatInstrument.""" + filenames = { + "UV": input_dataset.data_dir.joinpath("argo_float_uv.nc"), + "S": input_dataset.data_dir.joinpath("argo_float_s.nc"), + "T": input_dataset.data_dir.joinpath("argo_float_t.nc"), + } + variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=False, + allow_time_extrapolation=False, + ) + + def simulate( + self, + argo_floats: list[ArgoFloat], + out_path: str | Path, + outputdt: timedelta, + endtime: datetime | None = None, + ) -> None: + """Simulate Argo float measurements.""" + DT = 10.0 # dt of Argo float simulation integrator + + if len(argo_floats) == 0: + print( + "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset = self.load_input_data() + + # define parcel particles + argo_float_particleset = ParticleSet( + fieldset=fieldset, + pclass=_ArgoParticle, + lat=[argo.spacetime.location.lat for argo in argo_floats], + lon=[argo.spacetime.location.lon for argo in argo_floats], + depth=[argo.min_depth for argo in argo_floats], + time=[argo.spacetime.time for argo in argo_floats], + min_depth=[argo.min_depth for argo in argo_floats], + max_depth=[argo.max_depth for argo in argo_floats], + drift_depth=[argo.drift_depth for argo in argo_floats], + vertical_speed=[argo.vertical_speed for argo in argo_floats], + cycle_days=[argo.cycle_days for argo in argo_floats], + drift_days=[argo.drift_days for argo in argo_floats], + ) + + # define output file for the simulation + out_file = argo_float_particleset.ParticleFile( + name=out_path, outputdt=outputdt, chunks=[len(argo_float_particleset), 100] + ) + + # get earliest between fieldset end time and provide end time + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + if endtime is None: + actual_endtime = fieldset_endtime + elif endtime > fieldset_endtime: + print("WARN: Requested end time later than fieldset end time.") + actual_endtime = fieldset_endtime + else: + actual_endtime = np.timedelta64(endtime) + + # execute simulation + argo_float_particleset.execute( + [ + _argo_float_vertical_movement, + AdvectionRK4, + _keep_at_surface, + _check_error, + ], + endtime=actual_endtime, + dt=DT, + output_file=out_file, + verbose_progress=True, ) - # TODO when Parcels supports it this check can be removed. - return - - # define parcel particles - argo_float_particleset = ParticleSet( - fieldset=fieldset, - pclass=_ArgoParticle, - lat=[argo.spacetime.location.lat for argo in argo_floats], - lon=[argo.spacetime.location.lon for argo in argo_floats], - depth=[argo.min_depth for argo in argo_floats], - time=[argo.spacetime.time for argo in argo_floats], - min_depth=[argo.min_depth for argo in argo_floats], - max_depth=[argo.max_depth for argo in argo_floats], - drift_depth=[argo.drift_depth for argo in argo_floats], - vertical_speed=[argo.vertical_speed for argo in argo_floats], - cycle_days=[argo.cycle_days for argo in argo_floats], - drift_days=[argo.drift_days for argo in argo_floats], - ) - - # define output file for the simulation - out_file = argo_float_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(argo_float_particleset), 100] - ) - - # get earliest between fieldset end time and provide end time - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: - actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: - print("WARN: Requested end time later than fieldset end time.") - actual_endtime = fieldset_endtime - else: - actual_endtime = np.timedelta64(endtime) - - # execute simulation - argo_float_particleset.execute( - [ - _argo_float_vertical_movement, - AdvectionRK4, - _keep_at_surface, - _check_error, - ], - endtime=actual_endtime, - dt=DT, - output_file=out_file, - verbose_progress=True, - ) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index dfdb9c6a..2fab4795 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -1,11 +1,10 @@ import abc -from collections.abc import Callable from datetime import timedelta -from pathlib import Path import copernicusmarine import yaspin +from parcels import Field, FieldSet from virtualship.models import SpaceTimeRegion from virtualship.utils import ship_spinner @@ -37,7 +36,6 @@ def __init__( @abc.abstractmethod def get_datasets_dict(self) -> dict: """Get parameters for instrument's variable(s) specific data download.""" - ... def download_data(self) -> None: """Download data for the instrument using copernicusmarine.""" @@ -69,41 +67,89 @@ def download_data(self) -> None: class Instrument(abc.ABC): - """Base class for instruments.""" + """Base class for instruments and their simulation.""" def __init__( self, - name: str, - config, input_dataset: InputDataset, - kernels: list[Callable], + filenames: dict, + variables: dict, + add_bathymetry: bool, + allow_time_extrapolation: bool, + bathymetry_file: str = "bathymetry.nc", ): """Initialise instrument.""" - self.name = name - self.config = config self.input_data = input_dataset - self.kernels = kernels + self.name = input_dataset.name + self.directory = input_dataset.data_dir + self.filenames = filenames + self.variables = variables + self.dimensions = { + "lon": "longitude", + "lat": "latitude", + "time": "time", + "depth": "depth", + } # same dimensions for all instruments + self.bathymetry_file = self.directory.joinpath(bathymetry_file) + self.add_bathymetry = add_bathymetry + self.allow_time_extrapolation = allow_time_extrapolation + + def load_input_data(self) -> FieldSet: + """Load and return the input data as a FieldSet for the instrument.""" + # TODO: this should mean can delete input_data.py! + + # TODO: hopefully simulate_measurements can also be removed! And maybe the list of e.g. ctds ('measurements') to run can be added to higher level like do_expedition.py...? I think as they already do... + + # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? - # def load_fieldset(self): - # """Load fieldset for simulation.""" - # # paths = self.input_data.get_fieldset_paths() - # ... + # TODO: what do I need to do about automatic registration of Instrument classes...? - def get_output_path(self, output_dir: Path) -> Path: - """Get output path for results.""" - return output_dir / f"{self.name}.zarr" + # TODO: tests will need updating...! - def run(self): + # TODO: think about combining InputDataset and Instrument classes together! + + try: + fieldset = FieldSet.from_netcdf( + self.filenames, + self.variables, + self.dimensions, + allow_time_extrapolation=self.allow_time_extrapolation, + ) + except FileNotFoundError as e: + raise FileNotFoundError( + f"Input data for instrument {self.name} not found. Have you run the `virtualship fetch` command??" + ) from e + + # interpolation methods + for var in self.variables: + getattr(fieldset, var).interp_method = "linear_invdist_land_tracer" + # depth negative + for g in fieldset.gridset.grids: + g.negate_depth() + # bathymetry data + if self.add_bathymetry: + bathymetry_field = Field.from_netcdf( + self.bathymetry_file, + self.bathymetry_variables, + self.bathymetry_dimensions, + ) + bathymetry_field.data = -bathymetry_field.data + fieldset.add_field(bathymetry_field) + fieldset.computeTimeChunk(0, 1) # read in data already + return fieldset + + @abc.abstractmethod + def simulate(self): + """Simulate instrument measurements.""" + + def run(self, *args, **kwargs): """Run instrument simulation.""" + # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! + with yaspin( text=f"Simulating {self.name} measurements... ", side="right", spinner=ship_spinner, ) as spinner: - self.simulate() + self.simulate(*args, **kwargs) spinner.ok("✅") - - @abc.abstractmethod - def simulate(self): - """Simulate instrument measurements.""" - ... diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index cd8fd330..15e81041 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -5,11 +5,11 @@ import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset +from parcels import JITParticle, ParticleSet, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType -from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.models import Spacetime +from virtualship.utils import register_input_dataset @dataclass @@ -56,7 +56,7 @@ def _ctd_cast(particle, fieldset, time): particle.delete() -@register_instrument(InstrumentType.CTD) +@register_input_dataset(InstrumentType.CTD) class CTDInputDataset(InputDataset): """Input dataset for CTD instrument.""" @@ -94,102 +94,101 @@ def get_datasets_dict(self) -> dict: } -# class CTDInstrument(instruments.Instrument): -# """CTD instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(CTD.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_ctd( - fieldset: FieldSet, - out_path: str | Path, - ctds: list[CTD], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of CTDs in a fieldset. - - :param fieldset: The fieldset to simulate the CTDs in. - :param out_path: The path to write the results to. - :param ctds: A list of CTDs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided CTDs, fieldset, are not compatible with this function. - """ - WINCH_SPEED = 1.0 # sink and rise speed in m/s - DT = 10.0 # dt of CTD simulation integrator - - if len(ctds) == 0: - print( - "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) +class CTDInstrument(Instrument): + """CTD instrument class.""" - # deploy time for all ctds should be later than fieldset start time - if not all( - [np.datetime64(ctd.spacetime.time) >= fieldset_starttime for ctd in ctds] + def __init__( + self, + input_dataset: InputDataset, ): - raise ValueError("CTD deployed before fieldset starts.") - - # depth the ctd will go to. shallowest between ctd max depth and bathymetry. - max_depths = [ - max( - ctd.max_depth, - fieldset.bathymetry.eval( - z=0, y=ctd.spacetime.location.lat, x=ctd.spacetime.location.lon, time=0 - ), + """Initialize CTDInstrument.""" + filenames = { + "S": input_dataset.data_dir.joinpath(f"{input_dataset.name}_s.nc"), + "T": input_dataset.data_dir.joinpath(f"{input_dataset.name}_t.nc"), + } + variables = {"S": "so", "T": "thetao"} + + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=True, + allow_time_extrapolation=True, ) - for ctd in ctds - ] - # CTD depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): - raise ValueError( - f"CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + def simulate( + self, ctds: list[CTD], out_path: str | Path, outputdt: timedelta + ) -> None: + """Simulate CTD measurements.""" + WINCH_SPEED = 1.0 # sink and rise speed in m/s + DT = 10.0 # dt of CTD simulation integrator + + if len(ctds) == 0: + print( + "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset = self.load_input_data() + + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + + # deploy time for all ctds should be later than fieldset start time + if not all( + [np.datetime64(ctd.spacetime.time) >= fieldset_starttime for ctd in ctds] + ): + raise ValueError("CTD deployed before fieldset starts.") + + # depth the ctd will go to. shallowest between ctd max depth and bathymetry. + max_depths = [ + max( + ctd.max_depth, + fieldset.bathymetry.eval( + z=0, + y=ctd.spacetime.location.lat, + x=ctd.spacetime.location.lon, + time=0, + ), + ) + for ctd in ctds + ] + + # CTD depth can not be too shallow, because kernel would break. + # This shallow is not useful anyway, no need to support. + if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): + raise ValueError( + f"CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + ) + + # define parcel particles + ctd_particleset = ParticleSet( + fieldset=fieldset, + pclass=_CTDParticle, + lon=[ctd.spacetime.location.lon for ctd in ctds], + lat=[ctd.spacetime.location.lat for ctd in ctds], + depth=[ctd.min_depth for ctd in ctds], + time=[ctd.spacetime.time for ctd in ctds], + max_depth=max_depths, + min_depth=[ctd.min_depth for ctd in ctds], + winch_speed=[WINCH_SPEED for _ in ctds], ) - # define parcel particles - ctd_particleset = ParticleSet( - fieldset=fieldset, - pclass=_CTDParticle, - lon=[ctd.spacetime.location.lon for ctd in ctds], - lat=[ctd.spacetime.location.lat for ctd in ctds], - depth=[ctd.min_depth for ctd in ctds], - time=[ctd.spacetime.time for ctd in ctds], - max_depth=max_depths, - min_depth=[ctd.min_depth for ctd in ctds], - winch_speed=[WINCH_SPEED for _ in ctds], - ) - - # define output file for the simulation - out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt) - - # execute simulation - ctd_particleset.execute( - [_sample_salinity, _sample_temperature, _ctd_cast], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they resurface - if len(ctd_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + # define output file for the simulation + out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt) + + # execute simulation + ctd_particleset.execute( + [_sample_salinity, _sample_temperature, _ctd_cast], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, ) + + # there should be no particles left, as they delete themselves when they resurface + if len(ctd_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + ) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 92f717db..85bf02f5 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -5,11 +5,11 @@ import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset +from parcels import JITParticle, ParticleSet, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -81,7 +81,7 @@ def _ctd_bgc_cast(particle, fieldset, time): particle.delete() -@register_instrument(InstrumentType.CTD_BGC) +@register_input_dataset(InstrumentType.CTD_BGC) class CTD_BGCInputDataset(InputDataset): """Input dataset object for CTD_BGC instrument.""" @@ -149,117 +149,128 @@ def get_datasets_dict(self) -> dict: } -# class CTD_BGCInstrument(instruments.Instrument): -# """CTD_BGC instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(CTD_BGC.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_ctd_bgc( - fieldset: FieldSet, - out_path: str | Path, - ctd_bgcs: list[CTD_BGC], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of BGC CTDs in a fieldset. - - :param fieldset: The fieldset to simulate the BGC CTDs in. - :param out_path: The path to write the results to. - :param ctds: A list of BGC CTDs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided BGC CTDs, fieldset, are not compatible with this function. - """ - WINCH_SPEED = 1.0 # sink and rise speed in m/s - DT = 10.0 # dt of CTD simulation integrator - - if len(ctd_bgcs) == 0: - print( - "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return +@register_instrument(InstrumentType.CTD_BGC) +class CTD_BGCInstrument(Instrument): + """CTD_BGC instrument class.""" - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + def __init__( + self, + input_dataset: InputDataset, + ): + """Initialize CTD_BGCInstrument.""" + filenames = { + "o2": input_dataset.data_dir.joinpath("ctd_bgc_o2.nc"), + "chl": input_dataset.data_dir.joinpath("ctd_bgc_chl.nc"), + "no3": input_dataset.data_dir.joinpath("ctd_bgc_no3.nc"), + "po4": input_dataset.data_dir.joinpath("ctd_bgc_po4.nc"), + "ph": input_dataset.data_dir.joinpath("ctd_bgc_ph.nc"), + "phyc": input_dataset.data_dir.joinpath("ctd_bgc_phyc.nc"), + "zooc": input_dataset.data_dir.joinpath("ctd_bgc_zooc.nc"), + "nppv": input_dataset.data_dir.joinpath("ctd_bgc_nppv.nc"), + } + variables = { + "o2": "o2", + "chl": "chl", + "no3": "no3", + "po4": "po4", + "ph": "ph", + "phyc": "phyc", + "zooc": "zooc", + "nppv": "nppv", + } + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=True, + allow_time_extrapolation=True, + ) - # deploy time for all ctds should be later than fieldset start time - if not all( - [ - np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime + def simulate( + self, ctd_bgcs: list[CTD_BGC], out_path: str | Path, outputdt: timedelta + ) -> None: + """Simulate BGC CTD measurements using Parcels.""" + WINCH_SPEED = 1.0 # sink and rise speed in m/s + DT = 10.0 # dt of CTD simulation integrator + + if len(ctd_bgcs) == 0: + print( + "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset = self.load_input_data() + + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + + # deploy time for all ctds should be later than fieldset start time + if not all( + [ + np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime + for ctd_bgc in ctd_bgcs + ] + ): + raise ValueError("BGC CTD deployed before fieldset starts.") + + # depth the bgc ctd will go to. shallowest between bgc ctd max depth and bathymetry. + max_depths = [ + max( + ctd_bgc.max_depth, + fieldset.bathymetry.eval( + z=0, + y=ctd_bgc.spacetime.location.lat, + x=ctd_bgc.spacetime.location.lon, + time=0, + ), + ) for ctd_bgc in ctd_bgcs ] - ): - raise ValueError("BGC CTD deployed before fieldset starts.") - - # depth the bgc ctd will go to. shallowest between bgc ctd max depth and bathymetry. - max_depths = [ - max( - ctd_bgc.max_depth, - fieldset.bathymetry.eval( - z=0, - y=ctd_bgc.spacetime.location.lat, - x=ctd_bgc.spacetime.location.lon, - time=0, - ), - ) - for ctd_bgc in ctd_bgcs - ] - # CTD depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): - raise ValueError( - f"BGC CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + # CTD depth can not be too shallow, because kernel would break. + # This shallow is not useful anyway, no need to support. + if not all([max_depth <= -DT * WINCH_SPEED for max_depth in max_depths]): + raise ValueError( + f"BGC CTD max_depth or bathymetry shallower than maximum {-DT * WINCH_SPEED}" + ) + + # define parcel particles + ctd_bgc_particleset = ParticleSet( + fieldset=fieldset, + pclass=_CTD_BGCParticle, + lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in ctd_bgcs], + lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in ctd_bgcs], + depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], + time=[ctd_bgc.spacetime.time for ctd_bgc in ctd_bgcs], + max_depth=max_depths, + min_depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], + winch_speed=[WINCH_SPEED for _ in ctd_bgcs], ) - # define parcel particles - ctd_bgc_particleset = ParticleSet( - fieldset=fieldset, - pclass=_CTD_BGCParticle, - lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in ctd_bgcs], - lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in ctd_bgcs], - depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - time=[ctd_bgc.spacetime.time for ctd_bgc in ctd_bgcs], - max_depth=max_depths, - min_depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - winch_speed=[WINCH_SPEED for _ in ctd_bgcs], - ) - - # define output file for the simulation - out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt) - - # execute simulation - ctd_bgc_particleset.execute( - [ - _sample_o2, - _sample_chlorophyll, - _sample_nitrate, - _sample_phosphate, - _sample_ph, - _sample_phytoplankton, - _sample_primary_production, - _ctd_bgc_cast, - ], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they resurface - if len(ctd_bgc_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before BGC CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + # define output file for the simulation + out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt) + + # execute simulation + ctd_bgc_particleset.execute( + [ + _sample_o2, + _sample_chlorophyll, + _sample_nitrate, + _sample_phosphate, + _sample_ph, + _sample_phytoplankton, + _sample_primary_production, + _ctd_bgc_cast, + ], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, ) + + # there should be no particles left, as they delete themselves when they resurface + if len(ctd_bgc_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before BGC CTD resurfaced. This most likely means the field time dimension did not match the simulation time span." + ) diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 4ca0d087..186383ce 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -5,11 +5,11 @@ import numpy as np -from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset +from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -43,7 +43,7 @@ def _check_lifetime(particle, fieldset, time): particle.delete() -@register_instrument(InstrumentType.DRIFTER) +@register_input_dataset(InstrumentType.DRIFTER) class DrifterInputDataset(InputDataset): """Input dataset for Drifter instrument.""" @@ -83,91 +83,91 @@ def get_datasets_dict(self) -> dict: } -# class DrifterInstrument(instruments.Instrument): -# """Drifter instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(Drifter.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_drifters( - fieldset: FieldSet, - out_path: str | Path, - drifters: list[Drifter], - outputdt: timedelta, - dt: timedelta, - endtime: datetime | None = None, -) -> None: - """ - Use Parcels to simulate a set of drifters in a fieldset. - - :param fieldset: The fieldset to simulate the Drifters in. - :param out_path: The path to write the results to. - :param drifters: A list of drifters to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation. - :param dt: Dt for integration. - :param endtime: Stop at this time, or if None, continue until the end of the fieldset or until all drifters ended. If this is earlier than the last drifter ended or later than the end of the fieldset, a warning will be printed. - """ - if len(drifters) == 0: - print( - "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - # define parcel particles - drifter_particleset = ParticleSet( - fieldset=fieldset, - pclass=_DrifterParticle, - lat=[drifter.spacetime.location.lat for drifter in drifters], - lon=[drifter.spacetime.location.lon for drifter in drifters], - depth=[drifter.depth for drifter in drifters], - time=[drifter.spacetime.time for drifter in drifters], - has_lifetime=[1 if drifter.lifetime is not None else 0 for drifter in drifters], - lifetime=[ - 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() - for drifter in drifters - ], - ) - - # define output file for the simulation - out_file = drifter_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(drifter_particleset), 100] - ) - - # get earliest between fieldset end time and provide end time - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: - actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: - print("WARN: Requested end time later than fieldset end time.") - actual_endtime = fieldset_endtime - else: - actual_endtime = np.timedelta64(endtime) - - # execute simulation - drifter_particleset.execute( - [AdvectionRK4, _sample_temperature, _check_lifetime], - endtime=actual_endtime, - dt=dt, - output_file=out_file, - verbose_progress=True, - ) - - # if there are more particles left than the number of drifters with an indefinite endtime, warn the user - if len(drifter_particleset.particledata) > len( - [d for d in drifters if d.lifetime is None] +@register_instrument(InstrumentType.DRIFTER) +class DrifterInstrument(Instrument): + """Drifter instrument class.""" + + def __init__( + self, + input_dataset: InputDataset, ): - print( - "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." + """Initialize DrifterInstrument.""" + filenames = { + "UV": input_dataset.data_dir.joinpath("drifter_uv.nc"), + "T": input_dataset.data_dir.joinpath("drifter_t.nc"), + } + variables = {"UV": ["uo", "vo"], "T": "thetao"} + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=False, + allow_time_extrapolation=False, ) + + def simulate( + self, + drifters: list[Drifter], + out_path: str | Path, + outputdt: timedelta, + dt: timedelta, + endtime: datetime | None = None, + ) -> None: + """Simulate Drifter measurements.""" + if len(drifters) == 0: + print( + "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." + ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset = self.load_input_data() + + # define parcel particles + drifter_particleset = ParticleSet( + fieldset=fieldset, + pclass=_DrifterParticle, + lat=[drifter.spacetime.location.lat for drifter in drifters], + lon=[drifter.spacetime.location.lon for drifter in drifters], + depth=[drifter.depth for drifter in drifters], + time=[drifter.spacetime.time for drifter in drifters], + has_lifetime=[ + 1 if drifter.lifetime is not None else 0 for drifter in drifters + ], + lifetime=[ + 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() + for drifter in drifters + ], + ) + + # define output file for the simulation + out_file = drifter_particleset.ParticleFile( + name=out_path, outputdt=outputdt, chunks=[len(drifter_particleset), 100] + ) + + # get earliest between fieldset end time and provide end time + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + if endtime is None: + actual_endtime = fieldset_endtime + elif endtime > fieldset_endtime: + print("WARN: Requested end time later than fieldset end time.") + actual_endtime = fieldset_endtime + else: + actual_endtime = np.timedelta64(endtime) + + # execute simulation + drifter_particleset.execute( + [AdvectionRK4, _sample_temperature, _check_lifetime], + endtime=actual_endtime, + dt=dt, + output_file=out_file, + verbose_progress=True, + ) + + # if there are more particles left than the number of drifters with an indefinite endtime, warn the user + if len(drifter_particleset.particledata) > len( + [d for d in drifters if d.lifetime is None] + ): + print( + "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." + ) diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index accfb5b3..371b4485 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -4,11 +4,11 @@ import numpy as np -from parcels import FieldSet, ParticleSet, ScipyParticle, Variable -from virtualship.instruments.base import InputDataset +from parcels import ParticleSet, ScipyParticle, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -36,7 +36,7 @@ def _sample_temperature(particle, fieldset, time): particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] -@register_instrument(InstrumentType.UNDERWATER_ST) +@register_input_dataset(InstrumentType.UNDERWATER_ST) class Underwater_STInputDataset(InputDataset): """Input dataset for Underwater_ST instrument.""" @@ -76,67 +76,61 @@ def get_datasets_dict(self) -> dict: } -# class Underwater_STInstrument(instruments.Instrument): -# """Underwater_ST instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(Underwater_ST.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_ship_underwater_st( - fieldset: FieldSet, - out_path: str | Path, - depth: float, - sample_points: list[Spacetime], -) -> None: - """ - Use Parcels to simulate underway data, measuring salinity and temperature at the given depth along the ship track in a fieldset. - - :param fieldset: The fieldset to simulate the sampling in. - :param out_path: The path to write the results to. - :param depth: The depth at which to measure. 0 is water surface, negative is into the water. - :param sample_points: The places and times to sample at. - """ - sample_points.sort(key=lambda p: p.time) - - particleset = ParticleSet.from_list( - fieldset=fieldset, - pclass=_ShipSTParticle, - lon=0.0, # initial lat/lon are irrelevant and will be overruled later - lat=0.0, - depth=depth, - time=0, # same for time - ) - - # define output file for the simulation - # outputdt set to infinie as we want to just want to write at the end of every call to 'execute' - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - - # iterate over each point, manually set lat lon time, then - # execute the particle set for one step, performing one set of measurement - for point in sample_points: - particleset.lon_nextloop[:] = point.location.lon - particleset.lat_nextloop[:] = point.location.lat - particleset.time_nextloop[:] = fieldset.time_origin.reltime( - np.datetime64(point.time) +@register_instrument(InstrumentType.UNDERWATER_ST) +class Underwater_STInstrument(Instrument): + """Underwater_ST instrument class.""" + + def __init__( + self, + input_dataset: InputDataset, + ): + """Initialize Underwater_STInstrument.""" + filenames = { + "S": input_dataset.data_dir.joinpath(f"{input_dataset.name}_s.nc"), + "T": input_dataset.data_dir.joinpath(f"{input_dataset.name}_t.nc"), + } + variables = {"S": "so", "T": "thetao"} + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=False, + allow_time_extrapolation=True, ) - # perform one step using the particleset - # dt and runtime are set so exactly one step is made. - particleset.execute( - [_sample_salinity, _sample_temperature], - dt=1, - runtime=1, - verbose_progress=False, - output_file=out_file, + def simulate( + self, + out_path: str | Path, + depth: float, + sample_points: list[Spacetime], + ) -> None: + """Simulate underway salinity and temperature measurements.""" + sample_points.sort(key=lambda p: p.time) + + fieldset = self.load_input_data() + + particleset = ParticleSet.from_list( + fieldset=fieldset, + pclass=_ShipSTParticle, + lon=0.0, + lat=0.0, + depth=depth, + time=0, ) + + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + + for point in sample_points: + particleset.lon_nextloop[:] = point.location.lon + particleset.lat_nextloop[:] = point.location.lat + particleset.time_nextloop[:] = fieldset.time_origin.reltime( + np.datetime64(point.time) + ) + + particleset.execute( + [_sample_salinity, _sample_temperature], + dt=1, + runtime=1, + verbose_progress=False, + output_file=out_file, + ) diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index f5ec5fd0..c2fec98d 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -5,11 +5,11 @@ import numpy as np -from parcels import FieldSet, JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset +from parcels import JITParticle, ParticleSet, Variable +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_instrument +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -57,7 +57,7 @@ def _xbt_cast(particle, fieldset, time): particle_ddepth = particle.max_depth - particle.depth -@register_instrument(InstrumentType.XBT) +@register_input_dataset(InstrumentType.XBT) class XBTInputDataset(InputDataset): """Input dataset for XBT instrument.""" @@ -102,106 +102,105 @@ def get_datasets_dict(self) -> dict: } -# class XBTInstrument(instruments.Instrument): -# """XBT instrument class.""" - -# def __init__( -# self, -# config, -# input_dataset, -# kernels, -# ): -# """Initialise with instrument's name.""" -# super().__init__(XBT.name, config, input_dataset, kernels) - -# def simulate(self): -# """Simulate measurements.""" -# ... - - -def simulate_xbt( - fieldset: FieldSet, - out_path: str | Path, - xbts: list[XBT], - outputdt: timedelta, -) -> None: - """ - Use Parcels to simulate a set of XBTs in a fieldset. - - :param fieldset: The fieldset to simulate the XBTs in. - :param out_path: The path to write the results to. - :param xbts: A list of XBTs to simulate. - :param outputdt: Interval which dictates the update frequency of file output during simulation - :raises ValueError: Whenever provided XBTs, fieldset, are not compatible with this function. - """ - DT = 10.0 # dt of XBT simulation integrator - - if len(xbts) == 0: - print( - "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." - ) - # TODO when Parcels supports it this check can be removed. - return - - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) +@register_instrument(InstrumentType.XBT) +class XBTInstrument(Instrument): + """XBT instrument class.""" - # deploy time for all xbts should be later than fieldset start time - if not all( - [np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts] + def __init__( + self, + input_dataset: InputDataset, ): - raise ValueError("XBT deployed before fieldset starts.") - - # depth the xbt will go to. shallowest between xbt max depth and bathymetry. - max_depths = [ - max( - xbt.max_depth, - fieldset.bathymetry.eval( - z=0, y=xbt.spacetime.location.lat, x=xbt.spacetime.location.lon, time=0 - ), + """Initialize XBTInstrument.""" + filenames = { + "UV": input_dataset.data_dir.joinpath("ship_uv.nc"), + "S": input_dataset.data_dir.joinpath("ship_s.nc"), + "T": input_dataset.data_dir.joinpath("ship_t.nc"), + } + variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} + super().__init__( + input_dataset, + filenames, + variables, + add_bathymetry=True, + allow_time_extrapolation=True, ) - for xbt in xbts - ] - # initial fall speeds - initial_fall_speeds = [xbt.fall_speed for xbt in xbts] - - # XBT depth can not be too shallow, because kernel would break. - # This shallow is not useful anyway, no need to support. - # TODO: should this be more informative? Is "maximum" right? Should tell user can't use XBT here? - for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): - if not max_depth <= -DT * fall_speed: - raise ValueError( - f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}" + def simulate( + self, + xbts: list[XBT], + out_path: str | Path, + outputdt: timedelta, + ) -> None: + """Simulate XBT measurements.""" + DT = 10.0 # dt of XBT simulation integrator + + if len(xbts) == 0: + print( + "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." ) + # TODO when Parcels supports it this check can be removed. + return + + fieldset = self.load_input_data() + + fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) + fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + + # deploy time for all xbts should be later than fieldset start time + if not all( + [np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts] + ): + raise ValueError("XBT deployed before fieldset starts.") + + # depth the xbt will go to. shallowest between xbt max depth and bathymetry. + max_depths = [ + max( + xbt.max_depth, + fieldset.bathymetry.eval( + z=0, + y=xbt.spacetime.location.lat, + x=xbt.spacetime.location.lon, + time=0, + ), + ) + for xbt in xbts + ] + + # initial fall speeds + initial_fall_speeds = [xbt.fall_speed for xbt in xbts] + + # XBT depth can not be too shallow, because kernel would break. + for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): + if not max_depth <= -DT * fall_speed: + raise ValueError( + f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}" + ) + + # define xbt particles + xbt_particleset = ParticleSet( + fieldset=fieldset, + pclass=_XBTParticle, + lon=[xbt.spacetime.location.lon for xbt in xbts], + lat=[xbt.spacetime.location.lat for xbt in xbts], + depth=[xbt.min_depth for xbt in xbts], + time=[xbt.spacetime.time for xbt in xbts], + max_depth=max_depths, + min_depth=[xbt.min_depth for xbt in xbts], + fall_speed=[xbt.fall_speed for xbt in xbts], + ) - # define xbt particles - xbt_particleset = ParticleSet( - fieldset=fieldset, - pclass=_XBTParticle, - lon=[xbt.spacetime.location.lon for xbt in xbts], - lat=[xbt.spacetime.location.lat for xbt in xbts], - depth=[xbt.min_depth for xbt in xbts], - time=[xbt.spacetime.time for xbt in xbts], - max_depth=max_depths, - min_depth=[xbt.min_depth for xbt in xbts], - fall_speed=[xbt.fall_speed for xbt in xbts], - ) - - # define output file for the simulation - out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt) + out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt) - # execute simulation - xbt_particleset.execute( - [_sample_temperature, _xbt_cast], - endtime=fieldset_endtime, - dt=DT, - verbose_progress=False, - output_file=out_file, - ) - - # there should be no particles left, as they delete themselves when they finish profiling - if len(xbt_particleset.particledata) != 0: - raise ValueError( - "Simulation ended before XBT finished profiling. This most likely means the field time dimension did not match the simulation time span." + xbt_particleset.execute( + [_sample_temperature, _xbt_cast], + endtime=fieldset_endtime, + dt=DT, + verbose_progress=False, + output_file=out_file, ) + + # there should be no particles left, as they delete themselves when they finish profiling + if len(xbt_particleset.particledata) != 0: + raise ValueError( + "Simulation ended before XBT finished profiling. This most likely means the field time dimension did not match the simulation time span." + ) diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index d7559fd0..9add2c1b 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -17,7 +17,6 @@ if TYPE_CHECKING: from parcels import FieldSet - from virtualship.expedition.input_data import InputData projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") @@ -88,7 +87,6 @@ class Schedule(pydantic.BaseModel): def verify( self, ship_speed: float, - input_data: InputData | None, *, check_space_time_region: bool = False, ignore_missing_fieldsets: bool = False, @@ -139,19 +137,20 @@ def verify( # check if all waypoints are in water # this is done by picking an arbitrary provided fieldset and checking if UV is not zero + # TODO: this may need to be done with generic bathymetry data, now that removed InputData! # get all available fieldsets available_fieldsets = [] - if input_data is not None: - fieldsets = [ - input_data.adcp_fieldset, - input_data.argo_float_fieldset, - input_data.ctd_fieldset, - input_data.drifter_fieldset, - input_data.ship_underwater_st_fieldset, - ] - for fs in fieldsets: - if fs is not None: - available_fieldsets.append(fs) + # if input_data is not None: + # fieldsets = [ + # input_data.adcp_fieldset, + # input_data.argo_float_fieldset, + # input_data.ctd_fieldset, + # input_data.drifter_fieldset, + # input_data.ship_underwater_st_fieldset, + # ] + # for fs in fieldsets: + # if fs is not None: + # available_fieldsets.append(fs) # check if there are any fieldsets, else it's an error if len(available_fieldsets) == 0: diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index e1054da1..6ffaefe7 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -240,17 +240,31 @@ def _get_expedition(expedition_dir: Path) -> Expedition: ], ) -# InstrumentType -> InputDataset registry and registration utilities. -INSTRUMENT_INPUT_DATASET_MAP = {} + +# InstrumentType -> InputDataset and Instrument registry and registration utilities. +INPUT_DATASET_MAP = {} +INSTRUMENT_CLASS_MAP = {} + + +def register_input_dataset(instrument_type): + def decorator(cls): + INPUT_DATASET_MAP[instrument_type] = cls + return cls + + return decorator def register_instrument(instrument_type): def decorator(cls): - INSTRUMENT_INPUT_DATASET_MAP[instrument_type] = cls + INSTRUMENT_CLASS_MAP[instrument_type] = cls return cls return decorator def get_input_dataset_class(instrument_type): - return INSTRUMENT_INPUT_DATASET_MAP.get(instrument_type) + return INPUT_DATASET_MAP.get(instrument_type) + + +def get_instrument_class(instrument_type): + return INSTRUMENT_CLASS_MAP.get(instrument_type) From e96aa8a3775eed9cc45fea5553a7a66fa461aeaf Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:45:19 +0100 Subject: [PATCH 26/56] evaporate simulate_measurements.py; centralise run logic --- src/virtualship/expedition/do_expedition.py | 57 +++--- .../expedition/simulate_measurements.py | 162 ------------------ src/virtualship/instruments/adcp.py | 39 ++--- src/virtualship/instruments/argo_float.py | 66 ++++--- src/virtualship/instruments/base.py | 21 ++- src/virtualship/instruments/ctd.py | 42 ++--- src/virtualship/instruments/ctd_bgc.py | 71 ++++---- src/virtualship/instruments/drifter.py | 62 ++++--- .../instruments/ship_underwater_st.py | 32 ++-- src/virtualship/instruments/xbt.py | 55 +++--- 10 files changed, 211 insertions(+), 396 deletions(-) delete mode 100644 src/virtualship/expedition/simulate_measurements.py diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index ef5b6037..c4bc6783 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -44,17 +44,12 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> # verify that schedule and checkpoint match checkpoint.verify(expedition.schedule) - # load fieldsets - _load_input_data = [] # TEMPORARY! - loaded_input_data = _load_input_data( - expedition_dir=expedition_dir, - expedition=expedition, - input_data=input_data, - ) - print("\n---- WAYPOINT VERIFICATION ----") # verify schedule is valid + # TODO: needs updating when .verify() updated to not need input_data + + loaded_input_data = [] # TODO: TEMPORARY! expedition.schedule.verify( expedition.ship_config.ship_speed_knots, loaded_input_data ) @@ -87,48 +82,34 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> print("\n----- EXPEDITION SUMMARY ------") - # calculate expedition cost in US$ - assert expedition.schedule.waypoints[0].time is not None, ( - "First waypoint has no time. This should not be possible as it should have been verified before." - ) - time_past = schedule_results.time - expedition.schedule.waypoints[0].time - cost = expedition_cost(schedule_results, time_past) - with open(expedition_dir.joinpath("results", "cost.txt"), "w") as file: - file.writelines(f"cost: {cost} US$") - print(f"\nExpedition duration: {time_past}\nExpedition cost: US$ {cost:,.0f}.") + # expedition cost in US$ + _write_expedition_cost(expedition, schedule_results, expedition_dir) print("\n--- MEASUREMENT SIMULATIONS ---") # simulate measurements print("\nSimulating measurements. This may take a while...\n") - # TODO: this is where XYZInstrument.run() could be called instead of simulate_measurements!? - # TODO: this time maybe looping through measurements to simulate in some form... - # TODO: first in explicit per instrument, then think about whether can be automated more...not the end of the world if just have to explain in documentation that changes must be made here... - instruments_in_expedition = expedition.get_instruments() for itype in instruments_in_expedition: + # get instrument class instrument_class = get_instrument_class(itype) if instrument_class is None: raise RuntimeError(f"No instrument class found for type {itype}.") + # get measurements to simulate for this instrument measurements = schedule_results.measurements_to_simulate.get(itype.name.lower()) - instrument_class.run( - expedition_dir.joinpath("results", f"{itype.name.lower()}.zarr"), + # initialise instrument + instrument = instrument_class(expedition=expedition, directory=expedition_dir) + + # run simulation + instrument.run( measurements=measurements, - fieldset=loaded_input_data.get_fieldset_for_instrument_type(itype), - expedition=expedition, + out_path=expedition_dir.joinpath("results", f"{itype.name.lower()}.zarr"), ) - # simulate_measurements( - # expedition_dir, - # expedition.instruments_config, - # loaded_input_data, - # schedule_results.measurements_to_simulate, - # ) - print("\nAll measurement simulations are complete.") print("\n----- EXPEDITION RESULTS ------") @@ -150,3 +131,15 @@ def _load_checkpoint(expedition_dir: Path) -> Checkpoint | None: def _save_checkpoint(checkpoint: Checkpoint, expedition_dir: Path) -> None: file_path = expedition_dir.joinpath(CHECKPOINT) checkpoint.to_yaml(file_path) + + +def _write_expedition_cost(expedition, schedule_results, expedition_dir): + """Calculate the expedition cost, write it to a file, and print summary.""" + assert expedition.schedule.waypoints[0].time is not None, ( + "First waypoint has no time. This should not be possible as it should have been verified before." + ) + time_past = schedule_results.time - expedition.schedule.waypoints[0].time + cost = expedition_cost(schedule_results, time_past) + with open(expedition_dir.joinpath("results", "cost.txt"), "w") as file: + file.writelines(f"cost: {cost} US$") + print(f"\nExpedition duration: {time_past}\nExpedition cost: US$ {cost:,.0f}.") diff --git a/src/virtualship/expedition/simulate_measurements.py b/src/virtualship/expedition/simulate_measurements.py deleted file mode 100644 index 6cb2e488..00000000 --- a/src/virtualship/expedition/simulate_measurements.py +++ /dev/null @@ -1,162 +0,0 @@ -"""simulate_measurements function.""" - -from __future__ import annotations - -import logging -from datetime import timedelta -from pathlib import Path -from typing import TYPE_CHECKING - -from yaspin import yaspin - -from virtualship.instruments.adcp import simulate_adcp -from virtualship.instruments.argo_float import simulate_argo_floats -from virtualship.instruments.ctd import simulate_ctd -from virtualship.instruments.ctd_bgc import simulate_ctd_bgc -from virtualship.instruments.drifter import simulate_drifters -from virtualship.instruments.ship_underwater_st import simulate_ship_underwater_st -from virtualship.instruments.xbt import simulate_xbt -from virtualship.models import InstrumentsConfig -from virtualship.utils import ship_spinner - -from .simulate_schedule import MeasurementsToSimulate - -if TYPE_CHECKING: - from .input_data import InputData - -# parcels logger (suppress INFO messages to prevent log being flooded) -external_logger = logging.getLogger("parcels.tools.loggers") -external_logger.setLevel(logging.WARNING) - - -def simulate_measurements( - expedition_dir: str | Path, - instruments_config: InstrumentsConfig, - input_data: InputData, - measurements: MeasurementsToSimulate, -) -> None: - """ - Simulate measurements using Parcels. - - Saves everything in expedition_dir/results. - - :param expedition_dir: Base directory of the expedition. - :param input_data: Input data for simulation. - :param measurements: The measurements to simulate. - :raises RuntimeError: In case fieldsets of configuration is not provided. Make sure to check this before calling this function. - """ - if isinstance(expedition_dir, str): - expedition_dir = Path(expedition_dir) - - if len(measurements.ship_underwater_sts) > 0: - if instruments_config.ship_underwater_st_config is None: - raise RuntimeError("No configuration for ship underwater ST provided.") - if input_data.ship_underwater_st_fieldset is None: - raise RuntimeError("No fieldset for ship underwater ST provided.") - with yaspin( - text="Simulating onboard temperature and salinity measurements... ", - side="right", - spinner=ship_spinner, - ) as spinner: - simulate_ship_underwater_st( - fieldset=input_data.ship_underwater_st_fieldset, - out_path=expedition_dir.joinpath("results", "ship_underwater_st.zarr"), - depth=-2, - sample_points=measurements.ship_underwater_sts, - ) - spinner.ok("✅") - - if len(measurements.adcps) > 0: - if instruments_config.adcp_config is None: - raise RuntimeError("No configuration for ADCP provided.") - if input_data.adcp_fieldset is None: - raise RuntimeError("No fieldset for ADCP provided.") - with yaspin( - text="Simulating onboard ADCP... ", side="right", spinner=ship_spinner - ) as spinner: - simulate_adcp( - fieldset=input_data.adcp_fieldset, - out_path=expedition_dir.joinpath("results", "adcp.zarr"), - max_depth=instruments_config.adcp_config.max_depth_meter, - min_depth=-5, - num_bins=instruments_config.adcp_config.num_bins, - sample_points=measurements.adcps, - ) - spinner.ok("✅") - - if len(measurements.ctds) > 0: - if instruments_config.ctd_config is None: - raise RuntimeError("No configuration for CTD provided.") - if input_data.ctd_fieldset is None: - raise RuntimeError("No fieldset for CTD provided.") - with yaspin( - text="Simulating CTD casts... ", side="right", spinner=ship_spinner - ) as spinner: - simulate_ctd( - out_path=expedition_dir.joinpath("results", "ctd.zarr"), - fieldset=input_data.ctd_fieldset, - ctds=measurements.ctds, - outputdt=timedelta(seconds=10), - ) - spinner.ok("✅") - - if len(measurements.ctd_bgcs) > 0: - if instruments_config.ctd_bgc_config is None: - raise RuntimeError("No configuration for CTD_BGC provided.") - if input_data.ctd_bgc_fieldset is None: - raise RuntimeError("No fieldset for CTD_BGC provided.") - with yaspin( - text="Simulating BGC CTD casts... ", side="right", spinner=ship_spinner - ) as spinner: - simulate_ctd_bgc( - out_path=expedition_dir.joinpath("results", "ctd_bgc.zarr"), - fieldset=input_data.ctd_bgc_fieldset, - ctd_bgcs=measurements.ctd_bgcs, - outputdt=timedelta(seconds=10), - ) - spinner.ok("✅") - - if len(measurements.xbts) > 0: - if instruments_config.xbt_config is None: - raise RuntimeError("No configuration for XBTs provided.") - if input_data.xbt_fieldset is None: - raise RuntimeError("No fieldset for XBTs provided.") - with yaspin( - text="Simulating XBTs... ", side="right", spinner=ship_spinner - ) as spinner: - simulate_xbt( - out_path=expedition_dir.joinpath("results", "xbts.zarr"), - fieldset=input_data.xbt_fieldset, - xbts=measurements.xbts, - outputdt=timedelta(seconds=1), - ) - spinner.ok("✅") - - if len(measurements.drifters) > 0: - print("Simulating drifters... ") - if instruments_config.drifter_config is None: - raise RuntimeError("No configuration for drifters provided.") - if input_data.drifter_fieldset is None: - raise RuntimeError("No fieldset for drifters provided.") - simulate_drifters( - out_path=expedition_dir.joinpath("results", "drifters.zarr"), - fieldset=input_data.drifter_fieldset, - drifters=measurements.drifters, - outputdt=timedelta(hours=5), - dt=timedelta(minutes=5), - endtime=None, - ) - - if len(measurements.argo_floats) > 0: - print("Simulating argo floats... ") - if instruments_config.argo_float_config is None: - raise RuntimeError("No configuration for argo floats provided.") - if input_data.argo_float_fieldset is None: - raise RuntimeError("No fieldset for argo floats provided.") - simulate_argo_floats( - out_path=expedition_dir.joinpath("results", "argo_floats.zarr"), - argo_floats=measurements.argo_floats, - fieldset=input_data.argo_float_fieldset, - outputdt=timedelta(minutes=5), - endtime=None, - ) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 5caab5bf..917da154 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from pathlib import Path from typing import ClassVar import numpy as np @@ -7,8 +6,10 @@ from parcels import ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType -from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import ( + register_input_dataset, + register_instrument, +) @dataclass @@ -73,37 +74,33 @@ def get_datasets_dict(self) -> dict: class ADCPInstrument(Instrument): """ADCP instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize ADCPInstrument.""" filenames = { - "UV": input_dataset.data_dir.joinpath(f"{input_dataset.name}_uv.nc"), + "UV": directory.joinpath(f"{name}_uv.nc"), } variables = {"UV": ["uo", "vo"]} super().__init__( - input_dataset, + ADCP.name, + expedition, + directory, filenames, variables, add_bathymetry=False, allow_time_extrapolation=True, ) - def simulate( - self, - out_path: str | Path, - max_depth: float, - min_depth: float, - num_bins: int, - sample_points: list[Spacetime], - ) -> None: + def simulate(self) -> None: """Simulate ADCP measurements.""" - sample_points.sort(key=lambda p: p.time) + MAX_DEPTH = self.expedition.instruments_config.adcp_config.max_depth_meter + MIN_DEPTH = -5.0 + NUM_BINS = self.instruments_config.adcp_config.num_bins + + self.measurements.sort(key=lambda p: p.time) fieldset = self.load_input_data() - bins = np.linspace(max_depth, min_depth, num_bins) + bins = np.linspace(MAX_DEPTH, MIN_DEPTH, NUM_BINS) num_particles = len(bins) particleset = ParticleSet.from_list( fieldset=fieldset, @@ -114,9 +111,9 @@ def simulate( time=0, ) - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + out_file = particleset.ParticleFile(name=self.out_path, outputdt=np.inf) - for point in sample_points: + for point in self.measurements: particleset.lon_nextloop[:] = point.location.lon particleset.lat_nextloop[:] = point.location.lat particleset.time_nextloop[:] = fieldset.time_origin.reltime( diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 66b25bdf..239b47de 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -1,7 +1,6 @@ import math from dataclasses import dataclass -from datetime import datetime, timedelta -from pathlib import Path +from datetime import timedelta from typing import ClassVar import numpy as np @@ -147,17 +146,17 @@ def get_datasets_dict(self) -> dict: "UVdata": { "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", "variables": ["uo", "vo"], - "output_filename": "argo_float_uv.nc", + "output_filename": f"{self.name}_uv.nc", }, "Sdata": { "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", "variables": ["so"], - "output_filename": "argo_float_s.nc", + "output_filename": f"{self.name}_s.nc", }, "Tdata": { "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", "variables": ["thetao"], - "output_filename": "argo_float_t.nc", + "output_filename": f"{self.name}_t.nc", }, } @@ -166,36 +165,31 @@ def get_datasets_dict(self) -> dict: class ArgoFloatInstrument(Instrument): """ArgoFloat instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize ArgoFloatInstrument.""" filenames = { - "UV": input_dataset.data_dir.joinpath("argo_float_uv.nc"), - "S": input_dataset.data_dir.joinpath("argo_float_s.nc"), - "T": input_dataset.data_dir.joinpath("argo_float_t.nc"), + "UV": directory.joinpath(f"{name}_uv.nc"), + "S": directory.joinpath(f"{name}_s.nc"), + "T": directory.joinpath(f"{name}_t.nc"), } variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} super().__init__( - input_dataset, + ArgoFloat.name, + expedition, + directory, filenames, variables, add_bathymetry=False, allow_time_extrapolation=False, ) - def simulate( - self, - argo_floats: list[ArgoFloat], - out_path: str | Path, - outputdt: timedelta, - endtime: datetime | None = None, - ) -> None: + def simulate(self) -> None: """Simulate Argo float measurements.""" DT = 10.0 # dt of Argo float simulation integrator + OUTPUT_DT = timedelta(minutes=5) + ENDTIME = None - if len(argo_floats) == 0: + if len(self.measurements) == 0: print( "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." ) @@ -208,32 +202,34 @@ def simulate( argo_float_particleset = ParticleSet( fieldset=fieldset, pclass=_ArgoParticle, - lat=[argo.spacetime.location.lat for argo in argo_floats], - lon=[argo.spacetime.location.lon for argo in argo_floats], - depth=[argo.min_depth for argo in argo_floats], - time=[argo.spacetime.time for argo in argo_floats], - min_depth=[argo.min_depth for argo in argo_floats], - max_depth=[argo.max_depth for argo in argo_floats], - drift_depth=[argo.drift_depth for argo in argo_floats], - vertical_speed=[argo.vertical_speed for argo in argo_floats], - cycle_days=[argo.cycle_days for argo in argo_floats], - drift_days=[argo.drift_days for argo in argo_floats], + lat=[argo.spacetime.location.lat for argo in self.measurements], + lon=[argo.spacetime.location.lon for argo in self.measurements], + depth=[argo.min_depth for argo in self.measurements], + time=[argo.spacetime.time for argo in self.measurements], + min_depth=[argo.min_depth for argo in self.measurements], + max_depth=[argo.max_depth for argo in self.measurements], + drift_depth=[argo.drift_depth for argo in self.measurements], + vertical_speed=[argo.vertical_speed for argo in self.measurements], + cycle_days=[argo.cycle_days for argo in self.measurements], + drift_days=[argo.drift_days for argo in self.measurements], ) # define output file for the simulation out_file = argo_float_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(argo_float_particleset), 100] + name=self.out_path, + outputdt=OUTPUT_DT, + chunks=[len(argo_float_particleset), 100], ) # get earliest between fieldset end time and provide end time fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: + if ENDTIME is None: actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: + elif ENDTIME > fieldset_endtime: print("WARN: Requested end time later than fieldset end time.") actual_endtime = fieldset_endtime else: - actual_endtime = np.timedelta64(endtime) + actual_endtime = np.timedelta64(ENDTIME) # execute simulation argo_float_particleset.execute( diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 2fab4795..13a2fb05 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -1,11 +1,12 @@ import abc from datetime import timedelta +from pathlib import Path import copernicusmarine import yaspin from parcels import Field, FieldSet -from virtualship.models import SpaceTimeRegion +from virtualship.models import Expedition, SpaceTimeRegion from virtualship.utils import ship_spinner @@ -71,7 +72,9 @@ class Instrument(abc.ABC): def __init__( self, - input_dataset: InputDataset, + name: str, + expedition: Expedition, + directory: Path | str, filenames: dict, variables: dict, add_bathymetry: bool, @@ -79,9 +82,9 @@ def __init__( bathymetry_file: str = "bathymetry.nc", ): """Initialise instrument.""" - self.input_data = input_dataset - self.name = input_dataset.name - self.directory = input_dataset.data_dir + self.name = name + self.expedition = expedition + self.directory = directory self.filenames = filenames self.variables = variables self.dimensions = { @@ -106,7 +109,7 @@ def load_input_data(self) -> FieldSet: # TODO: tests will need updating...! - # TODO: think about combining InputDataset and Instrument classes together! + # TODO: think about combining InputDataset and Instrument classes together! Or maybe not if they are better kept separate... try: fieldset = FieldSet.from_netcdf( @@ -139,10 +142,10 @@ def load_input_data(self) -> FieldSet: return fieldset @abc.abstractmethod - def simulate(self): + def simulate(self, measurements: list, out_path: str | Path): """Simulate instrument measurements.""" - def run(self, *args, **kwargs): + def run(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! @@ -151,5 +154,5 @@ def run(self, *args, **kwargs): side="right", spinner=ship_spinner, ) as spinner: - self.simulate(*args, **kwargs) + self.simulate(measurements, out_path) spinner.ok("✅") diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 15e81041..82cace94 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from datetime import timedelta -from pathlib import Path from typing import ClassVar import numpy as np @@ -97,33 +96,31 @@ def get_datasets_dict(self) -> dict: class CTDInstrument(Instrument): """CTD instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize CTDInstrument.""" filenames = { - "S": input_dataset.data_dir.joinpath(f"{input_dataset.name}_s.nc"), - "T": input_dataset.data_dir.joinpath(f"{input_dataset.name}_t.nc"), + "S": directory.data_dir.joinpath(f"{name}_s.nc"), + "T": directory.data_dir.joinpath(f"{name}_t.nc"), } variables = {"S": "so", "T": "thetao"} super().__init__( - input_dataset, + CTD.name, + expedition, + directory, filenames, variables, add_bathymetry=True, allow_time_extrapolation=True, ) - def simulate( - self, ctds: list[CTD], out_path: str | Path, outputdt: timedelta - ) -> None: + def simulate(self) -> None: """Simulate CTD measurements.""" WINCH_SPEED = 1.0 # sink and rise speed in m/s DT = 10.0 # dt of CTD simulation integrator + OUTPUT_DT = timedelta(seconds=10) # output dt for CTD simulation - if len(ctds) == 0: + if len(self.measurements) == 0: print( "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." ) @@ -137,7 +134,10 @@ def simulate( # deploy time for all ctds should be later than fieldset start time if not all( - [np.datetime64(ctd.spacetime.time) >= fieldset_starttime for ctd in ctds] + [ + np.datetime64(ctd.spacetime.time) >= fieldset_starttime + for ctd in self.measurements + ] ): raise ValueError("CTD deployed before fieldset starts.") @@ -152,7 +152,7 @@ def simulate( time=0, ), ) - for ctd in ctds + for ctd in self.measurements ] # CTD depth can not be too shallow, because kernel would break. @@ -166,17 +166,17 @@ def simulate( ctd_particleset = ParticleSet( fieldset=fieldset, pclass=_CTDParticle, - lon=[ctd.spacetime.location.lon for ctd in ctds], - lat=[ctd.spacetime.location.lat for ctd in ctds], - depth=[ctd.min_depth for ctd in ctds], - time=[ctd.spacetime.time for ctd in ctds], + lon=[ctd.spacetime.location.lon for ctd in self.measurements], + lat=[ctd.spacetime.location.lat for ctd in self.measurements], + depth=[ctd.min_depth for ctd in self.measurements], + time=[ctd.spacetime.time for ctd in self.measurements], max_depth=max_depths, - min_depth=[ctd.min_depth for ctd in ctds], - winch_speed=[WINCH_SPEED for _ in ctds], + min_depth=[ctd.min_depth for ctd in self.measurements], + winch_speed=[WINCH_SPEED for _ in self.measurements], ) # define output file for the simulation - out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt) + out_file = ctd_particleset.ParticleFile(name=self.out_path, outputdt=OUTPUT_DT) # execute simulation ctd_particleset.execute( diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 85bf02f5..b017e0f4 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from datetime import timedelta -from pathlib import Path from typing import ClassVar import numpy as np @@ -109,42 +108,42 @@ def get_datasets_dict(self) -> dict: "o2data": { "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", "variables": ["o2"], - "output_filename": "ctd_bgc_o2.nc", + "output_filename": f"{self.name}_o2.nc", }, "chlorodata": { "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", "variables": ["chl"], - "output_filename": "ctd_bgc_chl.nc", + "output_filename": f"{self.name}_chl.nc", }, "nitratedata": { "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", "variables": ["no3"], - "output_filename": "ctd_bgc_no3.nc", + "output_filename": f"{self.name}_no3.nc", }, "phosphatedata": { "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", "variables": ["po4"], - "output_filename": "ctd_bgc_po4.nc", + "output_filename": f"{self.name}_po4.nc", }, "phdata": { "dataset_id": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", "variables": ["ph"], - "output_filename": "ctd_bgc_ph.nc", + "output_filename": f"{self.name}_ph.nc", }, "phytoplanktondata": { "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", "variables": ["phyc"], - "output_filename": "ctd_bgc_phyc.nc", + "output_filename": f"{self.name}_phyc.nc", }, "zooplanktondata": { "dataset_id": "cmems_mod_glo_bgc-plankton_anfc_0.25deg_P1D-m", "variables": ["zooc"], - "output_filename": "ctd_bgc_zooc.nc", + "output_filename": f"{self.name}_zooc.nc", }, "primaryproductiondata": { "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", "variables": ["nppv"], - "output_filename": "ctd_bgc_nppv.nc", + "output_filename": f"{self.name}_nppv.nc", }, } @@ -153,20 +152,17 @@ def get_datasets_dict(self) -> dict: class CTD_BGCInstrument(Instrument): """CTD_BGC instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize CTD_BGCInstrument.""" filenames = { - "o2": input_dataset.data_dir.joinpath("ctd_bgc_o2.nc"), - "chl": input_dataset.data_dir.joinpath("ctd_bgc_chl.nc"), - "no3": input_dataset.data_dir.joinpath("ctd_bgc_no3.nc"), - "po4": input_dataset.data_dir.joinpath("ctd_bgc_po4.nc"), - "ph": input_dataset.data_dir.joinpath("ctd_bgc_ph.nc"), - "phyc": input_dataset.data_dir.joinpath("ctd_bgc_phyc.nc"), - "zooc": input_dataset.data_dir.joinpath("ctd_bgc_zooc.nc"), - "nppv": input_dataset.data_dir.joinpath("ctd_bgc_nppv.nc"), + "o2": directory.joinpath(f"{name}_o2.nc"), + "chl": directory.joinpath(f"{name}_chl.nc"), + "no3": directory.joinpath(f"{name}_no3.nc"), + "po4": directory.joinpath(f"{name}_po4.nc"), + "ph": directory.joinpath(f"{name}_ph.nc"), + "phyc": directory.joinpath(f"{name}_phyc.nc"), + "zooc": directory.joinpath(f"{name}_zooc.nc"), + "nppv": directory.joinpath(f"{name}_nppv.nc"), } variables = { "o2": "o2", @@ -179,21 +175,22 @@ def __init__( "nppv": "nppv", } super().__init__( - input_dataset, + CTD_BGC.name, + expedition, + directory, filenames, variables, add_bathymetry=True, allow_time_extrapolation=True, ) - def simulate( - self, ctd_bgcs: list[CTD_BGC], out_path: str | Path, outputdt: timedelta - ) -> None: + def simulate(self) -> None: """Simulate BGC CTD measurements using Parcels.""" WINCH_SPEED = 1.0 # sink and rise speed in m/s - DT = 10.0 # dt of CTD simulation integrator + DT = 10.0 # dt of CTD_BGC simulation integrator + OUTPUT_DT = timedelta(seconds=10) # output dt for CTD_BGC simulation - if len(ctd_bgcs) == 0: + if len(self.measurements) == 0: print( "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." ) @@ -209,7 +206,7 @@ def simulate( if not all( [ np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime - for ctd_bgc in ctd_bgcs + for ctd_bgc in self.measurements ] ): raise ValueError("BGC CTD deployed before fieldset starts.") @@ -225,7 +222,7 @@ def simulate( time=0, ), ) - for ctd_bgc in ctd_bgcs + for ctd_bgc in self.measurements ] # CTD depth can not be too shallow, because kernel would break. @@ -239,17 +236,19 @@ def simulate( ctd_bgc_particleset = ParticleSet( fieldset=fieldset, pclass=_CTD_BGCParticle, - lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in ctd_bgcs], - lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in ctd_bgcs], - depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - time=[ctd_bgc.spacetime.time for ctd_bgc in ctd_bgcs], + lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in self.measurements], + lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in self.measurements], + depth=[ctd_bgc.min_depth for ctd_bgc in self.measurements], + time=[ctd_bgc.spacetime.time for ctd_bgc in self.measurements], max_depth=max_depths, - min_depth=[ctd_bgc.min_depth for ctd_bgc in ctd_bgcs], - winch_speed=[WINCH_SPEED for _ in ctd_bgcs], + min_depth=[ctd_bgc.min_depth for ctd_bgc in self.measurements], + winch_speed=[WINCH_SPEED for _ in self.measurements], ) # define output file for the simulation - out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt) + out_file = ctd_bgc_particleset.ParticleFile( + name=self.out_path, outputdt=OUTPUT_DT + ) # execute simulation ctd_bgc_particleset.execute( diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 186383ce..72db065e 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -1,6 +1,5 @@ from dataclasses import dataclass -from datetime import datetime, timedelta -from pathlib import Path +from datetime import timedelta from typing import ClassVar import numpy as np @@ -73,12 +72,12 @@ def get_datasets_dict(self) -> dict: "UVdata": { "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", "variables": ["uo", "vo"], - "output_filename": "drifter_uv.nc", + "output_filename": f"{self.name}_uv.nc", }, "Tdata": { "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", "variables": ["thetao"], - "output_filename": "drifter_t.nc", + "output_filename": f"{self.name}_t.nc", }, } @@ -87,34 +86,30 @@ def get_datasets_dict(self) -> dict: class DrifterInstrument(Instrument): """Drifter instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize DrifterInstrument.""" filenames = { - "UV": input_dataset.data_dir.joinpath("drifter_uv.nc"), - "T": input_dataset.data_dir.joinpath("drifter_t.nc"), + "UV": directory.joinpath(f"{name}_uv.nc"), + "T": directory.joinpath(f"{name}_t.nc"), } variables = {"UV": ["uo", "vo"], "T": "thetao"} super().__init__( - input_dataset, + Drifter.name, + expedition, + directory, filenames, variables, add_bathymetry=False, allow_time_extrapolation=False, ) - def simulate( - self, - drifters: list[Drifter], - out_path: str | Path, - outputdt: timedelta, - dt: timedelta, - endtime: datetime | None = None, - ) -> None: + def simulate(self) -> None: """Simulate Drifter measurements.""" - if len(drifters) == 0: + OUTPUT_DT = timedelta(hours=5) + DT = timedelta(minutes=5) + ENDTIME = None + + if len(self.measurements) == 0: print( "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." ) @@ -127,46 +122,49 @@ def simulate( drifter_particleset = ParticleSet( fieldset=fieldset, pclass=_DrifterParticle, - lat=[drifter.spacetime.location.lat for drifter in drifters], - lon=[drifter.spacetime.location.lon for drifter in drifters], - depth=[drifter.depth for drifter in drifters], - time=[drifter.spacetime.time for drifter in drifters], + lat=[drifter.spacetime.location.lat for drifter in self.measurements], + lon=[drifter.spacetime.location.lon for drifter in self.measurements], + depth=[drifter.depth for drifter in self.measurements], + time=[drifter.spacetime.time for drifter in self.measurements], has_lifetime=[ - 1 if drifter.lifetime is not None else 0 for drifter in drifters + 1 if drifter.lifetime is not None else 0 + for drifter in self.measurements ], lifetime=[ 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() - for drifter in drifters + for drifter in self.measurements ], ) # define output file for the simulation out_file = drifter_particleset.ParticleFile( - name=out_path, outputdt=outputdt, chunks=[len(drifter_particleset), 100] + name=self.out_path, + outputdt=OUTPUT_DT, + chunks=[len(drifter_particleset), 100], ) # get earliest between fieldset end time and provide end time fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) - if endtime is None: + if ENDTIME is None: actual_endtime = fieldset_endtime - elif endtime > fieldset_endtime: + elif ENDTIME > fieldset_endtime: print("WARN: Requested end time later than fieldset end time.") actual_endtime = fieldset_endtime else: - actual_endtime = np.timedelta64(endtime) + actual_endtime = np.timedelta64(ENDTIME) # execute simulation drifter_particleset.execute( [AdvectionRK4, _sample_temperature, _check_lifetime], endtime=actual_endtime, - dt=dt, + dt=DT, output_file=out_file, verbose_progress=True, ) # if there are more particles left than the number of drifters with an indefinite endtime, warn the user if len(drifter_particleset.particledata) > len( - [d for d in drifters if d.lifetime is None] + [d for d in self.measurements if d.lifetime is None] ): print( "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 371b4485..9c0fc401 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from pathlib import Path from typing import ClassVar import numpy as np @@ -7,7 +6,6 @@ from parcels import ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType -from virtualship.models.spacetime import Spacetime from virtualship.utils import register_input_dataset, register_instrument @@ -80,32 +78,28 @@ def get_datasets_dict(self) -> dict: class Underwater_STInstrument(Instrument): """Underwater_ST instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize Underwater_STInstrument.""" filenames = { - "S": input_dataset.data_dir.joinpath(f"{input_dataset.name}_s.nc"), - "T": input_dataset.data_dir.joinpath(f"{input_dataset.name}_t.nc"), + "S": directory.joinpath(f"{name}_s.nc"), + "T": directory.joinpath(f"{name}_t.nc"), } variables = {"S": "so", "T": "thetao"} super().__init__( - input_dataset, + Underwater_ST.name, + expedition, + directory, filenames, variables, add_bathymetry=False, allow_time_extrapolation=True, ) - def simulate( - self, - out_path: str | Path, - depth: float, - sample_points: list[Spacetime], - ) -> None: + def simulate(self) -> None: """Simulate underway salinity and temperature measurements.""" - sample_points.sort(key=lambda p: p.time) + DEPTH = -2.0 + + self.measurements.sort(key=lambda p: p.time) fieldset = self.load_input_data() @@ -114,13 +108,13 @@ def simulate( pclass=_ShipSTParticle, lon=0.0, lat=0.0, - depth=depth, + depth=DEPTH, time=0, ) - out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) + out_file = particleset.ParticleFile(name=self.out_path, outputdt=np.inf) - for point in sample_points: + for point in self.measurements: particleset.lon_nextloop[:] = point.location.lon particleset.lat_nextloop[:] = point.location.lat particleset.time_nextloop[:] = fieldset.time_origin.reltime( diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index c2fec98d..7c916d98 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from datetime import timedelta -from pathlib import Path from typing import ClassVar import numpy as np @@ -87,17 +86,17 @@ def get_datasets_dict(self) -> dict: "UVdata": { "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", "variables": ["uo", "vo"], - "output_filename": "ship_uv.nc", + "output_filename": f"{self.name}_uv.nc", }, "Sdata": { "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", "variables": ["so"], - "output_filename": "ship_s.nc", + "output_filename": f"{self.name}_s.nc", }, "Tdata": { "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", "variables": ["thetao"], - "output_filename": "ship_t.nc", + "output_filename": f"{self.name}_t.nc", }, } @@ -106,35 +105,30 @@ def get_datasets_dict(self) -> dict: class XBTInstrument(Instrument): """XBT instrument class.""" - def __init__( - self, - input_dataset: InputDataset, - ): + def __init__(self, name, expedition, directory): """Initialize XBTInstrument.""" filenames = { - "UV": input_dataset.data_dir.joinpath("ship_uv.nc"), - "S": input_dataset.data_dir.joinpath("ship_s.nc"), - "T": input_dataset.data_dir.joinpath("ship_t.nc"), + "UV": directory.joinpath(f"{name}_uv.nc"), + "S": directory.joinpath(f"{name}_s.nc"), + "T": directory.joinpath(f"{name}_t.nc"), } variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} super().__init__( - input_dataset, + XBT.name, + expedition, + directory, filenames, variables, add_bathymetry=True, allow_time_extrapolation=True, ) - def simulate( - self, - xbts: list[XBT], - out_path: str | Path, - outputdt: timedelta, - ) -> None: + def simulate(self) -> None: """Simulate XBT measurements.""" DT = 10.0 # dt of XBT simulation integrator + OUTPUT_DT = timedelta(seconds=1) - if len(xbts) == 0: + if len(self.measurements) == 0: print( "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." ) @@ -148,7 +142,10 @@ def simulate( # deploy time for all xbts should be later than fieldset start time if not all( - [np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts] + [ + np.datetime64(xbt.spacetime.time) >= fieldset_starttime + for xbt in self.measurements + ] ): raise ValueError("XBT deployed before fieldset starts.") @@ -163,11 +160,11 @@ def simulate( time=0, ), ) - for xbt in xbts + for xbt in self.measurements ] # initial fall speeds - initial_fall_speeds = [xbt.fall_speed for xbt in xbts] + initial_fall_speeds = [xbt.fall_speed for xbt in self.measurements] # XBT depth can not be too shallow, because kernel would break. for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): @@ -180,16 +177,16 @@ def simulate( xbt_particleset = ParticleSet( fieldset=fieldset, pclass=_XBTParticle, - lon=[xbt.spacetime.location.lon for xbt in xbts], - lat=[xbt.spacetime.location.lat for xbt in xbts], - depth=[xbt.min_depth for xbt in xbts], - time=[xbt.spacetime.time for xbt in xbts], + lon=[xbt.spacetime.location.lon for xbt in self.measurements], + lat=[xbt.spacetime.location.lat for xbt in self.measurements], + depth=[xbt.min_depth for xbt in self.measurements], + time=[xbt.spacetime.time for xbt in self.measurements], max_depth=max_depths, - min_depth=[xbt.min_depth for xbt in xbts], - fall_speed=[xbt.fall_speed for xbt in xbts], + min_depth=[xbt.min_depth for xbt in self.measurements], + fall_speed=[xbt.fall_speed for xbt in self.measurements], ) - out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt) + out_file = xbt_particleset.ParticleFile(name=self.out_path, outputdt=OUTPUT_DT) xbt_particleset.execute( [_sample_temperature, _xbt_cast], From 3efa29bda70de2e3a22a287321142033c2fb5a7f Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:24:53 +0100 Subject: [PATCH 27/56] draft up check land using bathymetry --- src/virtualship/expedition/do_expedition.py | 4 +- src/virtualship/instruments/base.py | 6 -- src/virtualship/models/expedition.py | 70 +++++++++------------ 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index c4bc6783..7be947b2 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -48,10 +48,8 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> # verify schedule is valid # TODO: needs updating when .verify() updated to not need input_data - - loaded_input_data = [] # TODO: TEMPORARY! expedition.schedule.verify( - expedition.ship_config.ship_speed_knots, loaded_input_data + expedition.ship_config.ship_speed_knots, input_dir=input_data ) # simulate the schedule diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 13a2fb05..001dc8c5 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -99,14 +99,8 @@ def __init__( def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" - # TODO: this should mean can delete input_data.py! - - # TODO: hopefully simulate_measurements can also be removed! And maybe the list of e.g. ctds ('measurements') to run can be added to higher level like do_expedition.py...? I think as they already do... - # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? - # TODO: what do I need to do about automatic registration of Instrument classes...? - # TODO: tests will need updating...! # TODO: think about combining InputDataset and Instrument classes together! Or maybe not if they are better kept separate... diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 9add2c1b..8888f657 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -2,12 +2,15 @@ import itertools from datetime import datetime, timedelta +from pathlib import Path from typing import TYPE_CHECKING +import numpy as np import pydantic import pyproj import yaml +from parcels import Field from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.types import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta @@ -87,9 +90,9 @@ class Schedule(pydantic.BaseModel): def verify( self, ship_speed: float, + input_dir: str | Path | None, *, check_space_time_region: bool = False, - ignore_missing_fieldsets: bool = False, ) -> None: """ Verify the feasibility and correctness of the schedule's waypoints. @@ -102,9 +105,9 @@ def verify( 5. The ship can arrive on time at each waypoint given its speed. :param ship_speed: The ship's speed in knots. - :param input_data: An InputData object containing fieldsets used to check if waypoints are on water. + :param input_dir: The input directory containing necessary files. :param check_space_time_region: whether to check for missing space_time_region. - :param ignore_missing_fieldsets: whether to ignore warning for missing field sets. + # :param ignore_missing_fieldsets: whether to ignore warning for missing field sets. :raises PlanningError: If any of the verification checks fail, indicating infeasible or incorrect waypoints. :raises NotImplementedError: If an instrument in the schedule is not implemented. :return: None. The method doesn't return a value but raises exceptions if verification fails. @@ -134,46 +137,33 @@ def verify( f"Waypoint(s) {', '.join(f'#{i + 1}' for i in invalid_i)}: each waypoint should be timed after all previous waypoints", ) - # check if all waypoints are in water - # this is done by picking an arbitrary provided fieldset and checking if UV is not zero - - # TODO: this may need to be done with generic bathymetry data, now that removed InputData! - # get all available fieldsets - available_fieldsets = [] - # if input_data is not None: - # fieldsets = [ - # input_data.adcp_fieldset, - # input_data.argo_float_fieldset, - # input_data.ctd_fieldset, - # input_data.drifter_fieldset, - # input_data.ship_underwater_st_fieldset, - # ] - # for fs in fieldsets: - # if fs is not None: - # available_fieldsets.append(fs) - - # check if there are any fieldsets, else it's an error - if len(available_fieldsets) == 0: - if not ignore_missing_fieldsets: - print( - "Cannot verify because no fieldsets have been loaded. This is probably " - "because you are not using any instruments in your schedule. This is not a problem, " - "but carefully check your waypoint locations manually." + # check if all waypoints are in water using bathymetry data + # TODO: tests should be updated to check this! + land_waypoints = [] + if input_dir is not None: + bathymetry_path = input_dir.joinpath("bathymetry.nc") + try: + bathymetry_field = Field.from_netcdf( + bathymetry_path, + variables=("bathymetry", "deptho"), + dimensions={"lon": "longitude", "lat": "latitude"}, ) - - else: - # pick any - fieldset = available_fieldsets[0] - # get waypoints with 0 UV - land_waypoints = [ - (wp_i, wp) - for wp_i, wp in enumerate(self.waypoints) - if _is_on_land_zero_uv(fieldset, wp) - ] - # raise an error if there are any + except Exception as e: + raise FileNotFoundError( + "Bathymetry file not found in input data. Cannot verify waypoints are in water." + ) from e + for wp_i, wp in enumerate(self.waypoints): + bathy = bathymetry_field.eval( + 0, # time + 0, # depth (surface) + wp.location.lat, + wp.location.lon, + ) + if np.isnan(bathy) or bathy >= 0: + land_waypoints.append((wp_i, wp)) if len(land_waypoints) > 0: raise ScheduleError( - f"The following waypoints are on land: {['#' + str(wp_i) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" + f"The following waypoints are on land: {['#' + str(wp_i + 1) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" ) # check that ship will arrive on time at each waypoint (in case no unexpected event happen) From aa2d3095be32b63695e146e7dedfcdb2c3985eef Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:55:44 +0100 Subject: [PATCH 28/56] small bug fixes --- src/virtualship/cli/_fetch.py | 3 +-- src/virtualship/cli/_plan.py | 7 ++++--- src/virtualship/expedition/__init__.py | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index 67695695..e472eb9a 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -52,9 +52,8 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.verify( expedition.ship_config.ship_speed_knots, - input_data=None, + input_dir=None, check_space_time_region=True, - ignore_missing_fieldsets=True, ) space_time_region_hash = get_space_time_region_hash( diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index a071c38e..41709a5c 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -951,7 +951,9 @@ def copy_from_previous(self) -> None: if prev and curr: curr.value = prev.value - for instrument in InstrumentType: + for instrument in [ + inst for inst in InstrumentType if not inst.is_underway + ]: prev_switch = schedule_editor.query_one( f"#wp{self.index - 1}_{instrument.value}" ) @@ -1044,9 +1046,8 @@ def save_pressed(self) -> None: # verify schedule expedition_editor.expedition.schedule.verify( ship_speed_value, - input_data=None, + input_dir=None, check_space_time_region=True, - ignore_missing_fieldsets=True, ) expedition_saved = expedition_editor.save_changes() diff --git a/src/virtualship/expedition/__init__.py b/src/virtualship/expedition/__init__.py index 43d24844..dfa61028 100644 --- a/src/virtualship/expedition/__init__.py +++ b/src/virtualship/expedition/__init__.py @@ -1,9 +1,7 @@ """Everything for simulating an expedition.""" from .do_expedition import do_expedition -from .input_data import InputData __all__ = [ - "InputData", "do_expedition", ] From 588cab4fc6fde2eee6c3e73c80b53f778786b54e Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:23:28 +0100 Subject: [PATCH 29/56] patch copernicus product id search logic to new instrument classes, plus more debugging; verbose INFO is outstanding --- src/virtualship/cli/_fetch.py | 141 +------------ src/virtualship/cli/_plan.py | 3 +- src/virtualship/expedition/do_expedition.py | 29 ++- .../expedition/simulate_schedule.py | 22 ++- src/virtualship/instruments/adcp.py | 23 ++- src/virtualship/instruments/argo_float.py | 43 ++-- src/virtualship/instruments/base.py | 185 ++++++++++++++++-- src/virtualship/instruments/ctd.py | 23 +-- src/virtualship/instruments/ctd_bgc.py | 47 ++--- src/virtualship/instruments/drifter.py | 32 +-- .../instruments/ship_underwater_st.py | 19 +- src/virtualship/instruments/xbt.py | 27 +-- src/virtualship/models/expedition.py | 32 +-- 13 files changed, 339 insertions(+), 287 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index e472eb9a..a41687d0 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -7,11 +7,10 @@ from typing import TYPE_CHECKING import copernicusmarine -import numpy as np from copernicusmarine.core_functions.credentials_utils import InvalidUsernameOrPassword from pydantic import BaseModel -from virtualship.errors import CopernicusCatalogueError, IncompleteDownloadError +from virtualship.errors import IncompleteDownloadError from virtualship.utils import ( _dump_yaml, _generic_load_yaml, @@ -52,8 +51,9 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.verify( expedition.ship_config.ship_speed_knots, - input_dir=None, + data_dir=None, check_space_time_region=True, + ignore_missing_bathymetry=True, ) space_time_region_hash = get_space_time_region_hash( @@ -107,7 +107,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None coordinates_selection_method="outside", ) - # Only keep instruments present in the expedition + # download, only instruments present in the expedition for itype in instruments_in_expedition: input_dataset_class = get_input_dataset_class(itype) if input_dataset_class is None: @@ -225,136 +225,3 @@ def complete_download(download_path: Path) -> None: metadata = DownloadMetadata(download_complete=True, download_date=datetime.now()) metadata.to_yaml(download_metadata) return - - -def select_product_id( - physical: bool, - schedule_start: datetime, - schedule_end: datetime, - username: str, - password: str, - variable: str | None = None, # only needed for BGC datasets -) -> str: - """ - Determine which copernicus product id should be selected (reanalysis, reanalysis-interim, analysis & forecast), for prescribed schedule and physical vs. BGC. - - BGC is more complicated than physical products. Often (re)analysis period and variable dependent, hence more custom logic here. - """ - product_ids = { - "phys": { - "reanalysis": "cmems_mod_glo_phy_my_0.083deg_P1D-m", - "reanalysis_interim": "cmems_mod_glo_phy_myint_0.083deg_P1D-m", - "analysis": "cmems_mod_glo_phy_anfc_0.083deg_P1D-m", - }, - "bgc": { - "reanalysis": "cmems_mod_glo_bgc_my_0.25deg_P1D-m", - "reanalysis_interim": "cmems_mod_glo_bgc_myint_0.25deg_P1D-m", - "analysis": None, # will be set per variable - }, - } - - bgc_analysis_ids = { - "o2": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", - "chl": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "no3": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "po4": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "ph": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", - "phyc": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "nppv": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", - } - - # pH and phytoplankton variables are available as *monthly* products only in renalysis(_interim) period - monthly_bgc_reanalysis_ids = { - "ph": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", - "phyc": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", - } - monthly_bgc_reanalysis_interim_ids = { - "ph": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", - "phyc": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", - } - - key = "phys" if physical else "bgc" - selected_id = None - - for period, pid in product_ids[key].items(): - # for BGC analysis, set pid per variable - if key == "bgc" and period == "analysis": - if variable is None or variable not in bgc_analysis_ids: - continue - pid = bgc_analysis_ids[variable] - # for BGC reanalysis, check if requires monthly product - if ( - key == "bgc" - and period == "reanalysis" - and variable in monthly_bgc_reanalysis_ids - ): - monthly_pid = monthly_bgc_reanalysis_ids[variable] - ds_monthly = copernicusmarine.open_dataset( - monthly_pid, - username=username, - password=password, - ) - time_end_monthly = ds_monthly["time"][-1].values - if np.datetime64(schedule_end) <= time_end_monthly: - pid = monthly_pid - # for BGC reanalysis_interim, check if requires monthly product - if ( - key == "bgc" - and period == "reanalysis_interim" - and variable in monthly_bgc_reanalysis_interim_ids - ): - monthly_pid = monthly_bgc_reanalysis_interim_ids[variable] - ds_monthly = copernicusmarine.open_dataset( - monthly_pid, username=username, password=password - ) - time_end_monthly = ds_monthly["time"][-1].values - if np.datetime64(schedule_end) <= time_end_monthly: - pid = monthly_pid - if pid is None: - continue - ds = copernicusmarine.open_dataset(pid, username=username, password=password) - time_end = ds["time"][-1].values - if np.datetime64(schedule_end) <= time_end: - selected_id = pid - break - - if selected_id is None: - raise CopernicusCatalogueError( - "No suitable product found in the Copernicus Marine Catalogue for the scheduled time and variable." - ) - - # handle the rare situation where start time and end time span different products, which is possible for reanalysis and reanalysis_interim - # in this case, return the analysis product which spans far back enough - if start_end_in_product_timerange( - selected_id, schedule_start, schedule_end, username, password - ): - return selected_id - - else: - return ( - product_ids["phys"]["analysis"] if physical else bgc_analysis_ids[variable] - ) - - -def start_end_in_product_timerange( - selected_id: str, - schedule_start: datetime, - schedule_end: datetime, - username: str, - password: str, -) -> bool: - """Check schedule_start and schedule_end are both within a selected Copernicus product's time range.""" - ds_selected = copernicusmarine.open_dataset( - selected_id, username=username, password=password - ) - time_values = ds_selected["time"].values - time_min, time_max = np.min(time_values), np.max(time_values) - - if ( - np.datetime64(schedule_start) >= time_min - and np.datetime64(schedule_end) <= time_max - ): - return True - - else: - return False diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index 41709a5c..c27cb741 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -1046,8 +1046,9 @@ def save_pressed(self) -> None: # verify schedule expedition_editor.expedition.schedule.verify( ship_speed_value, - input_dir=None, + data_dir=None, check_space_time_region=True, + ignore_missing_bathymetry=True, ) expedition_saved = expedition_editor.save_changes() diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index 7be947b2..ec61b1b0 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -6,23 +6,31 @@ import pyproj +from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.models import Schedule -from virtualship.utils import CHECKPOINT, _get_expedition, get_instrument_class +from virtualship.utils import ( + CHECKPOINT, + _get_expedition, + get_instrument_class, +) from .checkpoint import Checkpoint from .expedition_cost import expedition_cost -from .simulate_schedule import ScheduleProblem, simulate_schedule +from .simulate_schedule import ( + MeasurementsToSimulate, + ScheduleProblem, + simulate_schedule, +) # projection used to sail between waypoints projection = pyproj.Geod(ellps="WGS84") -def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> None: +def do_expedition(expedition_dir: str | Path) -> None: """ Perform an expedition, providing terminal feedback and file output. :param expedition_dir: The base directory for the expedition. - :param input_data: Input data folder (override used for testing). """ print("\n╔═════════════════════════════════════════════════╗") print("║ VIRTUALSHIP EXPEDITION STATUS ║") @@ -47,11 +55,13 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> print("\n---- WAYPOINT VERIFICATION ----") # verify schedule is valid - # TODO: needs updating when .verify() updated to not need input_data - expedition.schedule.verify( - expedition.ship_config.ship_speed_knots, input_dir=input_data + data_dir = get_existing_download( + expedition_dir, + get_space_time_region_hash(expedition.schedule.space_time_region), ) + expedition.schedule.verify(expedition.ship_config.ship_speed_knots, data_dir) + # simulate the schedule schedule_results = simulate_schedule( projection=projection, @@ -96,8 +106,9 @@ def do_expedition(expedition_dir: str | Path, input_data: Path | None = None) -> if instrument_class is None: raise RuntimeError(f"No instrument class found for type {itype}.") - # get measurements to simulate for this instrument - measurements = schedule_results.measurements_to_simulate.get(itype.name.lower()) + # get measurements to simulate + attr = MeasurementsToSimulate.get_attr_for_instrumenttype(itype) + measurements = getattr(schedule_results.measurements_to_simulate, attr) # initialise instrument instrument = instrument_class(expedition=expedition, directory=expedition_dir) diff --git a/src/virtualship/expedition/simulate_schedule.py b/src/virtualship/expedition/simulate_schedule.py index f8d142ea..3b8fa78a 100644 --- a/src/virtualship/expedition/simulate_schedule.py +++ b/src/virtualship/expedition/simulate_schedule.py @@ -4,6 +4,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta +from typing import ClassVar import pyproj @@ -39,7 +40,26 @@ class ScheduleProblem: @dataclass class MeasurementsToSimulate: - """The measurements to simulate, as concluded from schedule simulation.""" + """ + The measurements to simulate, as concluded from schedule simulation. + + Provides a mapping from InstrumentType to the correct attribute name for robust access. + """ + + _instrumenttype_to_attr: ClassVar[dict] = { + InstrumentType.ADCP: "adcps", + InstrumentType.UNDERWATER_ST: "ship_underwater_sts", + InstrumentType.ARGO_FLOAT: "argo_floats", + InstrumentType.DRIFTER: "drifters", + InstrumentType.CTD: "ctds", + InstrumentType.CTD_BGC: "ctd_bgcs", + InstrumentType.XBT: "xbts", + } + + @classmethod + def get_attr_for_instrumenttype(cls, instrument_type): + """Return the attribute name for a given InstrumentType.""" + return cls._instrumenttype_to_attr[instrument_type] adcps: list[Spacetime] = field(default_factory=list, init=False) ship_underwater_sts: list[Spacetime] = field(default_factory=list, init=False) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 917da154..db5be8ef 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -63,7 +63,7 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["uo", "vo"], "output_filename": f"{self.name}_uv.nc", }, @@ -74,12 +74,13 @@ def get_datasets_dict(self) -> dict: class ADCPInstrument(Instrument): """ADCP instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize ADCPInstrument.""" filenames = { - "UV": directory.joinpath(f"{name}_uv.nc"), + "U": f"{ADCP.name}_uv.nc", + "V": f"{ADCP.name}_uv.nc", } - variables = {"UV": ["uo", "vo"]} + variables = {"U": "uo", "V": "vo"} super().__init__( ADCP.name, expedition, @@ -90,13 +91,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=True, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate ADCP measurements.""" MAX_DEPTH = self.expedition.instruments_config.adcp_config.max_depth_meter MIN_DEPTH = -5.0 - NUM_BINS = self.instruments_config.adcp_config.num_bins + NUM_BINS = self.expedition.instruments_config.adcp_config.num_bins - self.measurements.sort(key=lambda p: p.time) + measurements.sort(key=lambda p: p.time) fieldset = self.load_input_data() @@ -105,15 +106,17 @@ def simulate(self) -> None: particleset = ParticleSet.from_list( fieldset=fieldset, pclass=_ADCPParticle, - lon=np.full(num_particles, 0.0), + lon=np.full( + num_particles, 0.0 + ), # initial lat/lon are irrelevant and will be overruled later.s lat=np.full(num_particles, 0.0), depth=bins, time=0, ) - out_file = particleset.ParticleFile(name=self.out_path, outputdt=np.inf) + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - for point in self.measurements: + for point in measurements: particleset.lon_nextloop[:] = point.location.lon particleset.lat_nextloop[:] = point.location.lat particleset.time_nextloop[:] = fieldset.time_origin.reltime( diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 239b47de..93aa9093 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -144,17 +144,17 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["uo", "vo"], "output_filename": f"{self.name}_uv.nc", }, "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["so"], "output_filename": f"{self.name}_s.nc", }, "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["thetao"], "output_filename": f"{self.name}_t.nc", }, @@ -165,14 +165,15 @@ def get_datasets_dict(self) -> dict: class ArgoFloatInstrument(Instrument): """ArgoFloat instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize ArgoFloatInstrument.""" filenames = { - "UV": directory.joinpath(f"{name}_uv.nc"), - "S": directory.joinpath(f"{name}_s.nc"), - "T": directory.joinpath(f"{name}_t.nc"), + "U": f"{ArgoFloat.name}_uv.nc", + "V": f"{ArgoFloat.name}_uv.nc", + "S": f"{ArgoFloat.name}_s.nc", + "T": f"{ArgoFloat.name}_t.nc", } - variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} + variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} super().__init__( ArgoFloat.name, expedition, @@ -183,13 +184,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=False, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate Argo float measurements.""" DT = 10.0 # dt of Argo float simulation integrator OUTPUT_DT = timedelta(minutes=5) ENDTIME = None - if len(self.measurements) == 0: + if len(measurements) == 0: print( "No Argo floats provided. Parcels currently crashes when providing an empty particle set, so no argo floats simulation will be done and no files will be created." ) @@ -202,21 +203,21 @@ def simulate(self) -> None: argo_float_particleset = ParticleSet( fieldset=fieldset, pclass=_ArgoParticle, - lat=[argo.spacetime.location.lat for argo in self.measurements], - lon=[argo.spacetime.location.lon for argo in self.measurements], - depth=[argo.min_depth for argo in self.measurements], - time=[argo.spacetime.time for argo in self.measurements], - min_depth=[argo.min_depth for argo in self.measurements], - max_depth=[argo.max_depth for argo in self.measurements], - drift_depth=[argo.drift_depth for argo in self.measurements], - vertical_speed=[argo.vertical_speed for argo in self.measurements], - cycle_days=[argo.cycle_days for argo in self.measurements], - drift_days=[argo.drift_days for argo in self.measurements], + lat=[argo.spacetime.location.lat for argo in measurements], + lon=[argo.spacetime.location.lon for argo in measurements], + depth=[argo.min_depth for argo in measurements], + time=[argo.spacetime.time for argo in measurements], + min_depth=[argo.min_depth for argo in measurements], + max_depth=[argo.max_depth for argo in measurements], + drift_depth=[argo.drift_depth for argo in measurements], + vertical_speed=[argo.vertical_speed for argo in measurements], + cycle_days=[argo.cycle_days for argo in measurements], + drift_days=[argo.drift_days for argo in measurements], ) # define output file for the simulation out_file = argo_float_particleset.ParticleFile( - name=self.out_path, + name=out_path, outputdt=OUTPUT_DT, chunks=[len(argo_float_particleset), 100], ) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 001dc8c5..6ae38cc0 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -3,12 +3,47 @@ from pathlib import Path import copernicusmarine -import yaspin +import numpy as np +from yaspin import yaspin from parcels import Field, FieldSet +from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash +from virtualship.errors import CopernicusCatalogueError from virtualship.models import Expedition, SpaceTimeRegion from virtualship.utils import ship_spinner +PRODUCT_IDS = { + "phys": { + "reanalysis": "cmems_mod_glo_phy_my_0.083deg_P1D-m", + "reanalysis_interim": "cmems_mod_glo_phy_myint_0.083deg_P1D-m", + "analysis": "cmems_mod_glo_phy_anfc_0.083deg_P1D-m", + }, + "bgc": { + "reanalysis": "cmems_mod_glo_bgc_my_0.25deg_P1D-m", + "reanalysis_interim": "cmems_mod_glo_bgc_myint_0.25deg_P1D-m", + "analysis": None, # will be set per variable + }, +} + +BGC_ANALYSIS_IDS = { + "o2": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "chl": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "no3": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "po4": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "ph": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", + "phyc": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "nppv": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", +} + +MONTHLY_BGC_REANALYSIS_IDS = { + "ph": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", + "phyc": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", +} +MONTHLY_BGC_REANALYSIS_INTERIM_IDS = { + "ph": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", + "phyc": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", +} + class InputDataset(abc.ABC): """Base class for instrument input datasets.""" @@ -39,7 +74,7 @@ def get_datasets_dict(self) -> dict: """Get parameters for instrument's variable(s) specific data download.""" def download_data(self) -> None: - """Download data for the instrument using copernicusmarine.""" + """Download data for the instrument using copernicusmarine, with correct product ID selection.""" parameter_args = dict( minimum_longitude=self.space_time_region.spatial_range.minimum_longitude - self.latlon_buffer, @@ -62,10 +97,117 @@ def download_data(self) -> None: ) datasets_args = self.get_datasets_dict() + for dataset in datasets_args.values(): - download_args = {**parameter_args, **dataset} + physical = dataset.get("physical") + if physical: + variable = None + else: + variable = dataset.get("variables")[0] # BGC variables, special case + + dataset_id = self._select_product_id( + physical=physical, + schedule_start=self.space_time_region.time_range.start_time, + schedule_end=self.space_time_region.time_range.end_time, + username=self.credentials["username"], + password=self.credentials["password"], + variable=variable, + ) + download_args = { + **parameter_args, + **{k: v for k, v in dataset.items() if k != "physical"}, + "dataset_id": dataset_id, + } copernicusmarine.subset(**download_args) + def _select_product_id( + self, + physical: bool, + schedule_start, + schedule_end, + username: str, + password: str, + variable: str | None = None, + ) -> str: + """Determine which copernicus product id should be selected (reanalysis, reanalysis-interim, analysis & forecast), for prescribed schedule and physical vs. BGC.""" + key = "phys" if physical else "bgc" + selected_id = None + + for period, pid in PRODUCT_IDS[key].items(): + # for BGC analysis, set pid per variable + if key == "bgc" and period == "analysis": + if variable is None or variable not in BGC_ANALYSIS_IDS: + continue + pid = BGC_ANALYSIS_IDS[variable] + # for BGC reanalysis, check if requires monthly product + if ( + key == "bgc" + and period == "reanalysis" + and variable in MONTHLY_BGC_REANALYSIS_IDS + ): + monthly_pid = MONTHLY_BGC_REANALYSIS_IDS[variable] + ds_monthly = copernicusmarine.open_dataset( + monthly_pid, + username=username, + password=password, + ) + time_end_monthly = ds_monthly["time"][-1].values + if np.datetime64(schedule_end) <= time_end_monthly: + pid = monthly_pid + # for BGC reanalysis_interim, check if requires monthly product + if ( + key == "bgc" + and period == "reanalysis_interim" + and variable in MONTHLY_BGC_REANALYSIS_INTERIM_IDS + ): + monthly_pid = MONTHLY_BGC_REANALYSIS_INTERIM_IDS[variable] + ds_monthly = copernicusmarine.open_dataset( + monthly_pid, username=username, password=password + ) + time_end_monthly = ds_monthly["time"][-1].values + if np.datetime64(schedule_end) <= time_end_monthly: + pid = monthly_pid + if pid is None: + continue + ds = copernicusmarine.open_dataset( + pid, username=username, password=password + ) + time_end = ds["time"][-1].values + if np.datetime64(schedule_end) <= time_end: + selected_id = pid + break + + if selected_id is None: + raise CopernicusCatalogueError( + "No suitable product found in the Copernicus Marine Catalogue for the scheduled time and variable." + ) + + def start_end_in_product_timerange( + selected_id, schedule_start, schedule_end, username, password + ): + ds_selected = copernicusmarine.open_dataset( + selected_id, username=username, password=password + ) + time_values = ds_selected["time"].values + import numpy as np + + time_min, time_max = np.min(time_values), np.max(time_values) + return ( + np.datetime64(schedule_start) >= time_min + and np.datetime64(schedule_end) <= time_max + ) + + if start_end_in_product_timerange( + selected_id, schedule_start, schedule_end, username, password + ): + return selected_id + else: + return ( + PRODUCT_IDS["phys"]["analysis"] + if physical + else BGC_ANALYSIS_IDS[variable] + ) + class Instrument(abc.ABC): """Base class for instruments and their simulation.""" @@ -93,21 +235,23 @@ def __init__( "time": "time", "depth": "depth", } # same dimensions for all instruments - self.bathymetry_file = self.directory.joinpath(bathymetry_file) + self.bathymetry_file = bathymetry_file self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? - - # TODO: tests will need updating...! - - # TODO: think about combining InputDataset and Instrument classes together! Or maybe not if they are better kept separate... + # TODO: tests need updating...! try: + data_dir = self._get_data_dir(self.directory) + joined_filepaths = { + key: data_dir.joinpath(filename) + for key, filename in self.filenames.items() + } fieldset = FieldSet.from_netcdf( - self.filenames, + joined_filepaths, self.variables, self.dimensions, allow_time_extrapolation=self.allow_time_extrapolation, @@ -118,7 +262,7 @@ def load_input_data(self) -> FieldSet: ) from e # interpolation methods - for var in self.variables: + for var in (v for v in self.variables if v not in ("U", "V")): getattr(fieldset, var).interp_method = "linear_invdist_land_tracer" # depth negative for g in fieldset.gridset.grids: @@ -126,17 +270,18 @@ def load_input_data(self) -> FieldSet: # bathymetry data if self.add_bathymetry: bathymetry_field = Field.from_netcdf( - self.bathymetry_file, - self.bathymetry_variables, - self.bathymetry_dimensions, + data_dir.joinpath(self.bathymetry_file), + variable=("bathymetry", "deptho"), + dimensions={"lon": "longitude", "lat": "latitude"}, ) bathymetry_field.data = -bathymetry_field.data fieldset.add_field(bathymetry_field) fieldset.computeTimeChunk(0, 1) # read in data already + return fieldset @abc.abstractmethod - def simulate(self, measurements: list, out_path: str | Path): + def simulate(self, data_dir: Path, measurements: list, out_path: str | Path): """Simulate instrument measurements.""" def run(self, measurements: list, out_path: str | Path) -> None: @@ -150,3 +295,15 @@ def run(self, measurements: list, out_path: str | Path) -> None: ) as spinner: self.simulate(measurements, out_path) spinner.ok("✅") + + def _get_data_dir(self, expedition_dir: Path) -> Path: + space_time_region_hash = get_space_time_region_hash( + self.expedition.schedule.space_time_region + ) + data_dir = get_existing_download(expedition_dir, space_time_region_hash) + + assert data_dir is not None, ( + "Input data hasn't been found. Have you run the `virtualship fetch` command?" + ) + + return data_dir diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 82cace94..c1981e06 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -8,7 +8,7 @@ from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models import Spacetime -from virtualship.utils import register_input_dataset +from virtualship.utils import register_input_dataset, register_instrument @dataclass @@ -81,26 +81,27 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["so"], "output_filename": f"{self.name}_s.nc", }, "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["thetao"], "output_filename": f"{self.name}_t.nc", }, } +@register_instrument(InstrumentType.CTD) class CTDInstrument(Instrument): """CTD instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize CTDInstrument.""" filenames = { - "S": directory.data_dir.joinpath(f"{name}_s.nc"), - "T": directory.data_dir.joinpath(f"{name}_t.nc"), + "S": f"{CTD.name}_s.nc", + "T": f"{CTD.name}_t.nc", } variables = {"S": "so", "T": "thetao"} @@ -114,13 +115,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=True, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate CTD measurements.""" WINCH_SPEED = 1.0 # sink and rise speed in m/s DT = 10.0 # dt of CTD simulation integrator OUTPUT_DT = timedelta(seconds=10) # output dt for CTD simulation - if len(self.measurements) == 0: + if len(measurements) == 0: print( "No CTDs provided. Parcels currently crashes when providing an empty particle set, so no CTD simulation will be done and no files will be created." ) @@ -136,7 +137,7 @@ def simulate(self) -> None: if not all( [ np.datetime64(ctd.spacetime.time) >= fieldset_starttime - for ctd in self.measurements + for ctd in measurements ] ): raise ValueError("CTD deployed before fieldset starts.") @@ -152,7 +153,7 @@ def simulate(self) -> None: time=0, ), ) - for ctd in self.measurements + for ctd in measurements ] # CTD depth can not be too shallow, because kernel would break. @@ -176,7 +177,7 @@ def simulate(self) -> None: ) # define output file for the simulation - out_file = ctd_particleset.ParticleFile(name=self.out_path, outputdt=OUTPUT_DT) + out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=OUTPUT_DT) # execute simulation ctd_particleset.execute( diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index b017e0f4..ed9ab9d9 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -106,42 +106,37 @@ def get_datasets_dict(self) -> dict: """Variable specific args for instrument.""" return { "o2data": { - "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["o2"], "output_filename": f"{self.name}_o2.nc", }, "chlorodata": { - "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["chl"], "output_filename": f"{self.name}_chl.nc", }, "nitratedata": { - "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["no3"], "output_filename": f"{self.name}_no3.nc", }, "phosphatedata": { - "dataset_id": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["po4"], "output_filename": f"{self.name}_po4.nc", }, "phdata": { - "dataset_id": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["ph"], "output_filename": f"{self.name}_ph.nc", }, "phytoplanktondata": { - "dataset_id": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["phyc"], "output_filename": f"{self.name}_phyc.nc", }, - "zooplanktondata": { - "dataset_id": "cmems_mod_glo_bgc-plankton_anfc_0.25deg_P1D-m", - "variables": ["zooc"], - "output_filename": f"{self.name}_zooc.nc", - }, "primaryproductiondata": { - "dataset_id": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "physical": False, "variables": ["nppv"], "output_filename": f"{self.name}_nppv.nc", }, @@ -152,17 +147,16 @@ def get_datasets_dict(self) -> dict: class CTD_BGCInstrument(Instrument): """CTD_BGC instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize CTD_BGCInstrument.""" filenames = { - "o2": directory.joinpath(f"{name}_o2.nc"), - "chl": directory.joinpath(f"{name}_chl.nc"), - "no3": directory.joinpath(f"{name}_no3.nc"), - "po4": directory.joinpath(f"{name}_po4.nc"), - "ph": directory.joinpath(f"{name}_ph.nc"), - "phyc": directory.joinpath(f"{name}_phyc.nc"), - "zooc": directory.joinpath(f"{name}_zooc.nc"), - "nppv": directory.joinpath(f"{name}_nppv.nc"), + "o2": f"{CTD_BGC.name}_o2.nc", + "chl": f"{CTD_BGC.name}_chl.nc", + "no3": f"{CTD_BGC.name}_no3.nc", + "po4": f"{CTD_BGC.name}_po4.nc", + "ph": f"{CTD_BGC.name}_ph.nc", + "phyc": f"{CTD_BGC.name}_phyc.nc", + "nppv": f"{CTD_BGC.name}_nppv.nc", } variables = { "o2": "o2", @@ -171,7 +165,6 @@ def __init__(self, name, expedition, directory): "po4": "po4", "ph": "ph", "phyc": "phyc", - "zooc": "zooc", "nppv": "nppv", } super().__init__( @@ -184,13 +177,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=True, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate BGC CTD measurements using Parcels.""" WINCH_SPEED = 1.0 # sink and rise speed in m/s DT = 10.0 # dt of CTD_BGC simulation integrator OUTPUT_DT = timedelta(seconds=10) # output dt for CTD_BGC simulation - if len(self.measurements) == 0: + if len(measurements) == 0: print( "No BGC CTDs provided. Parcels currently crashes when providing an empty particle set, so no BGC CTD simulation will be done and no files will be created." ) @@ -206,7 +199,7 @@ def simulate(self) -> None: if not all( [ np.datetime64(ctd_bgc.spacetime.time) >= fieldset_starttime - for ctd_bgc in self.measurements + for ctd_bgc in measurements ] ): raise ValueError("BGC CTD deployed before fieldset starts.") @@ -246,9 +239,7 @@ def simulate(self) -> None: ) # define output file for the simulation - out_file = ctd_bgc_particleset.ParticleFile( - name=self.out_path, outputdt=OUTPUT_DT - ) + out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=OUTPUT_DT) # execute simulation ctd_bgc_particleset.execute( diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 72db065e..93cd2631 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -70,12 +70,12 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["uo", "vo"], "output_filename": f"{self.name}_uv.nc", }, "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["thetao"], "output_filename": f"{self.name}_t.nc", }, @@ -86,13 +86,14 @@ def get_datasets_dict(self) -> dict: class DrifterInstrument(Instrument): """Drifter instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize DrifterInstrument.""" filenames = { - "UV": directory.joinpath(f"{name}_uv.nc"), - "T": directory.joinpath(f"{name}_t.nc"), + "U": f"{Drifter.name}_uv.nc", + "V": f"{Drifter.name}_uv.nc", + "T": f"{Drifter.name}_t.nc", } - variables = {"UV": ["uo", "vo"], "T": "thetao"} + variables = {"U": "uo", "V": "vo", "T": "thetao"} super().__init__( Drifter.name, expedition, @@ -103,13 +104,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=False, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate Drifter measurements.""" OUTPUT_DT = timedelta(hours=5) DT = timedelta(minutes=5) ENDTIME = None - if len(self.measurements) == 0: + if len(measurements) == 0: print( "No drifters provided. Parcels currently crashes when providing an empty particle set, so no drifter simulation will be done and no files will be created." ) @@ -122,23 +123,22 @@ def simulate(self) -> None: drifter_particleset = ParticleSet( fieldset=fieldset, pclass=_DrifterParticle, - lat=[drifter.spacetime.location.lat for drifter in self.measurements], - lon=[drifter.spacetime.location.lon for drifter in self.measurements], - depth=[drifter.depth for drifter in self.measurements], - time=[drifter.spacetime.time for drifter in self.measurements], + lat=[drifter.spacetime.location.lat for drifter in measurements], + lon=[drifter.spacetime.location.lon for drifter in measurements], + depth=[drifter.depth for drifter in measurements], + time=[drifter.spacetime.time for drifter in measurements], has_lifetime=[ - 1 if drifter.lifetime is not None else 0 - for drifter in self.measurements + 1 if drifter.lifetime is not None else 0 for drifter in measurements ], lifetime=[ 0 if drifter.lifetime is None else drifter.lifetime.total_seconds() - for drifter in self.measurements + for drifter in measurements ], ) # define output file for the simulation out_file = drifter_particleset.ParticleFile( - name=self.out_path, + name=out_path, outputdt=OUTPUT_DT, chunks=[len(drifter_particleset), 100], ) diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 9c0fc401..4160f474 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -62,12 +62,12 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["so"], "output_filename": f"{self.name}_s.nc", }, "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["thetao"], "output_filename": f"{self.name}_t.nc", }, @@ -78,11 +78,11 @@ def get_datasets_dict(self) -> dict: class Underwater_STInstrument(Instrument): """Underwater_ST instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize Underwater_STInstrument.""" filenames = { - "S": directory.joinpath(f"{name}_s.nc"), - "T": directory.joinpath(f"{name}_t.nc"), + "S": f"{Underwater_ST.name}_s.nc", + "T": f"{Underwater_ST.name}_t.nc", } variables = {"S": "so", "T": "thetao"} super().__init__( @@ -95,14 +95,13 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=True, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate underway salinity and temperature measurements.""" DEPTH = -2.0 - self.measurements.sort(key=lambda p: p.time) + measurements.sort(key=lambda p: p.time) fieldset = self.load_input_data() - particleset = ParticleSet.from_list( fieldset=fieldset, pclass=_ShipSTParticle, @@ -112,9 +111,9 @@ def simulate(self) -> None: time=0, ) - out_file = particleset.ParticleFile(name=self.out_path, outputdt=np.inf) + out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf) - for point in self.measurements: + for point in measurements: particleset.lon_nextloop[:] = point.location.lon particleset.lat_nextloop[:] = point.location.lat particleset.time_nextloop[:] = fieldset.time_origin.reltime( diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 7c916d98..918ef9a2 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -84,17 +84,17 @@ def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { "UVdata": { - "dataset_id": "cmems_mod_glo_phy-cur_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["uo", "vo"], "output_filename": f"{self.name}_uv.nc", }, "Sdata": { - "dataset_id": "cmems_mod_glo_phy-so_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["so"], "output_filename": f"{self.name}_s.nc", }, "Tdata": { - "dataset_id": "cmems_mod_glo_phy-thetao_anfc_0.083deg_PT6H-i", + "physical": True, "variables": ["thetao"], "output_filename": f"{self.name}_t.nc", }, @@ -105,14 +105,15 @@ def get_datasets_dict(self) -> dict: class XBTInstrument(Instrument): """XBT instrument class.""" - def __init__(self, name, expedition, directory): + def __init__(self, expedition, directory): """Initialize XBTInstrument.""" filenames = { - "UV": directory.joinpath(f"{name}_uv.nc"), - "S": directory.joinpath(f"{name}_s.nc"), - "T": directory.joinpath(f"{name}_t.nc"), + "U": f"{XBT.name}_uv.nc", + "V": f"{XBT.name}_uv.nc", + "S": f"{XBT.name}_s.nc", + "T": f"{XBT.name}_t.nc", } - variables = {"UV": ["uo", "vo"], "S": "so", "T": "thetao"} + variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} super().__init__( XBT.name, expedition, @@ -123,12 +124,12 @@ def __init__(self, name, expedition, directory): allow_time_extrapolation=True, ) - def simulate(self) -> None: + def simulate(self, measurements, out_path) -> None: """Simulate XBT measurements.""" DT = 10.0 # dt of XBT simulation integrator OUTPUT_DT = timedelta(seconds=1) - if len(self.measurements) == 0: + if len(measurements) == 0: print( "No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created." ) @@ -144,7 +145,7 @@ def simulate(self) -> None: if not all( [ np.datetime64(xbt.spacetime.time) >= fieldset_starttime - for xbt in self.measurements + for xbt in measurements ] ): raise ValueError("XBT deployed before fieldset starts.") @@ -160,7 +161,7 @@ def simulate(self) -> None: time=0, ), ) - for xbt in self.measurements + for xbt in measurements ] # initial fall speeds @@ -186,7 +187,7 @@ def simulate(self) -> None: fall_speed=[xbt.fall_speed for xbt in self.measurements], ) - out_file = xbt_particleset.ParticleFile(name=self.out_path, outputdt=OUTPUT_DT) + out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=OUTPUT_DT) xbt_particleset.execute( [_sample_temperature, _xbt_cast], diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 8888f657..c70de855 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -62,7 +62,7 @@ def get_instruments(self) -> set[InstrumentType]: instruments_in_expedition.append(InstrumentType.ADCP) if self.instruments_config.ship_underwater_st_config is not None: instruments_in_expedition.append(InstrumentType.UNDERWATER_ST) - return set(instruments_in_expedition) + return sorted(set(instruments_in_expedition), key=lambda x: x.name) except Exception as e: raise InstrumentsConfigError( "Underway instrument config attribute(s) are missing from YAML. Must be Config object or None." @@ -90,7 +90,8 @@ class Schedule(pydantic.BaseModel): def verify( self, ship_speed: float, - input_dir: str | Path | None, + data_dir: str | Path | None, + ignore_missing_bathymetry: bool = False, *, check_space_time_region: bool = False, ) -> None: @@ -103,14 +104,6 @@ def verify( 3. Waypoint times are in ascending order. 4. All waypoints are in water (not on land). 5. The ship can arrive on time at each waypoint given its speed. - - :param ship_speed: The ship's speed in knots. - :param input_dir: The input directory containing necessary files. - :param check_space_time_region: whether to check for missing space_time_region. - # :param ignore_missing_fieldsets: whether to ignore warning for missing field sets. - :raises PlanningError: If any of the verification checks fail, indicating infeasible or incorrect waypoints. - :raises NotImplementedError: If an instrument in the schedule is not implemented. - :return: None. The method doesn't return a value but raises exceptions if verification fails. """ print("\nVerifying route... ") @@ -139,18 +132,20 @@ def verify( # check if all waypoints are in water using bathymetry data # TODO: tests should be updated to check this! + # TODO: write test that checks that will flag when waypoint is on land!! [add to existing suite of fail .verify() tests in test_expedition.py] + # TODO: need to do an overhaul of the DATA which is in tests/expedition/expedition_dir - don't think it's currently suitable! land_waypoints = [] - if input_dir is not None: - bathymetry_path = input_dir.joinpath("bathymetry.nc") + if data_dir is not None: + bathymetry_path = data_dir.joinpath("bathymetry.nc") try: bathymetry_field = Field.from_netcdf( bathymetry_path, - variables=("bathymetry", "deptho"), + variable=("bathymetry", "deptho"), dimensions={"lon": "longitude", "lat": "latitude"}, ) except Exception as e: - raise FileNotFoundError( - "Bathymetry file not found in input data. Cannot verify waypoints are in water." + raise ScheduleError( + f"Problem loading bathymetry data (used to verify waypoints are in water): {e}" ) from e for wp_i, wp in enumerate(self.waypoints): bathy = bathymetry_field.eval( @@ -159,12 +154,17 @@ def verify( wp.location.lat, wp.location.lon, ) - if np.isnan(bathy) or bathy >= 0: + if np.isnan(bathy) or bathy <= 0: land_waypoints.append((wp_i, wp)) + if len(land_waypoints) > 0: raise ScheduleError( f"The following waypoints are on land: {['#' + str(wp_i + 1) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" ) + elif not ignore_missing_bathymetry: + raise ScheduleError( + "Cannot verify waypoints are in water as bathymetry data not found. Have you run `virtualship fetch` command?" + ) # check that ship will arrive on time at each waypoint (in case no unexpected event happen) time = self.waypoints[0].time From 1081fab71eb481d665a8f66c1d8dea3c09b72d53 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:01:08 +0100 Subject: [PATCH 30/56] adding U and V to instruments where missing --- src/virtualship/instruments/base.py | 19 ++++++------ src/virtualship/instruments/ctd.py | 29 +++++++++++++------ src/virtualship/instruments/ctd_bgc.py | 17 +++++++++-- .../instruments/ship_underwater_st.py | 10 ++++++- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 6ae38cc0..c45c71fe 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,13 +4,11 @@ import copernicusmarine import numpy as np -from yaspin import yaspin from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.errors import CopernicusCatalogueError from virtualship.models import Expedition, SpaceTimeRegion -from virtualship.utils import ship_spinner PRODUCT_IDS = { "phys": { @@ -48,6 +46,9 @@ class InputDataset(abc.ABC): """Base class for instrument input datasets.""" + # TODO: data download is performed per instrument (in `fetch`), which is a bit inefficient when some instruments can share dataa. + # TODO: However, future changes, with Parcels-v4 and copernicusmarine direct ingestion, will hopefully remove the need for fetch. + def __init__( self, name: str, @@ -288,13 +289,13 @@ def run(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! - with yaspin( - text=f"Simulating {self.name} measurements... ", - side="right", - spinner=ship_spinner, - ) as spinner: - self.simulate(measurements, out_path) - spinner.ok("✅") + # with yaspin( + # text=f"Simulating {self.name} measurements... ", + # side="right", + # spinner=ship_spinner, + # ) as spinner: + self.simulate(measurements, out_path) + # spinner.ok("✅") def _get_data_dir(self, expedition_dir: Path) -> Path: space_time_region_hash = get_space_time_region_hash( diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index c1981e06..ffc88922 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -80,6 +80,11 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { + "UVdata": { + "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "variables": ["uo", "vo"], + "output_filename": f"{self.name}_uv.nc", + }, "Sdata": { "physical": True, "variables": ["so"], @@ -100,10 +105,12 @@ class CTDInstrument(Instrument): def __init__(self, expedition, directory): """Initialize CTDInstrument.""" filenames = { + "U": f"{CTD.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "V": f"{CTD.name}_uv.nc", "S": f"{CTD.name}_s.nc", "T": f"{CTD.name}_t.nc", } - variables = {"S": "so", "T": "thetao"} + variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} super().__init__( CTD.name, @@ -130,8 +137,12 @@ def simulate(self, measurements, out_path) -> None: fieldset = self.load_input_data() - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + fieldset_starttime = fieldset.T.grid.time_origin.fulltime( + fieldset.T.grid.time_full[0] + ) + fieldset_endtime = fieldset.T.grid.time_origin.fulltime( + fieldset.T.grid.time_full[-1] + ) # deploy time for all ctds should be later than fieldset start time if not all( @@ -167,13 +178,13 @@ def simulate(self, measurements, out_path) -> None: ctd_particleset = ParticleSet( fieldset=fieldset, pclass=_CTDParticle, - lon=[ctd.spacetime.location.lon for ctd in self.measurements], - lat=[ctd.spacetime.location.lat for ctd in self.measurements], - depth=[ctd.min_depth for ctd in self.measurements], - time=[ctd.spacetime.time for ctd in self.measurements], + lon=[ctd.spacetime.location.lon for ctd in measurements], + lat=[ctd.spacetime.location.lat for ctd in measurements], + depth=[ctd.min_depth for ctd in measurements], + time=[ctd.spacetime.time for ctd in measurements], max_depth=max_depths, - min_depth=[ctd.min_depth for ctd in self.measurements], - winch_speed=[WINCH_SPEED for _ in self.measurements], + min_depth=[ctd.min_depth for ctd in measurements], + winch_speed=[WINCH_SPEED for _ in measurements], ) # define output file for the simulation diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index ed9ab9d9..1b702b6e 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -105,6 +105,11 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Variable specific args for instrument.""" return { + "UVdata": { + "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "variables": ["uo", "vo"], + "output_filename": f"{self.name}_uv.nc", + }, "o2data": { "physical": False, "variables": ["o2"], @@ -150,6 +155,8 @@ class CTD_BGCInstrument(Instrument): def __init__(self, expedition, directory): """Initialize CTD_BGCInstrument.""" filenames = { + "U": f"{CTD_BGC.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "V": f"{CTD_BGC.name}_uv.nc", "o2": f"{CTD_BGC.name}_o2.nc", "chl": f"{CTD_BGC.name}_chl.nc", "no3": f"{CTD_BGC.name}_no3.nc", @@ -159,6 +166,8 @@ def __init__(self, expedition, directory): "nppv": f"{CTD_BGC.name}_nppv.nc", } variables = { + "U": "uo", + "V": "vo", "o2": "o2", "chl": "chl", "no3": "no3", @@ -192,8 +201,12 @@ def simulate(self, measurements, out_path) -> None: fieldset = self.load_input_data() - fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0]) - fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) + fieldset_starttime = fieldset.o2.grid.time_origin.fulltime( + fieldset.o2.grid.time_full[0] + ) + fieldset_endtime = fieldset.o2.grid.time_origin.fulltime( + fieldset.o2.grid.time_full[-1] + ) # deploy time for all ctds should be later than fieldset start time if not all( diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 4160f474..2ffbd762 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -61,6 +61,11 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { + "UVdata": { + "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "variables": ["uo", "vo"], + "output_filename": f"{self.name}_uv.nc", + }, "Sdata": { "physical": True, "variables": ["so"], @@ -81,10 +86,13 @@ class Underwater_STInstrument(Instrument): def __init__(self, expedition, directory): """Initialize Underwater_STInstrument.""" filenames = { + "U": f"{Underwater_ST.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? + "V": f"{Underwater_ST.name}_uv.nc", "S": f"{Underwater_ST.name}_s.nc", "T": f"{Underwater_ST.name}_t.nc", } - variables = {"S": "so", "T": "thetao"} + variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} + super().__init__( Underwater_ST.name, expedition, From a0d7d2c64d033c2ac71c171a58c59aba3c61a4fb Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:07:43 +0100 Subject: [PATCH 31/56] enhanced error messaging for XBT in too shallow regions --- src/virtualship/instruments/xbt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 918ef9a2..b15c0e6f 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -165,26 +165,26 @@ def simulate(self, measurements, out_path) -> None: ] # initial fall speeds - initial_fall_speeds = [xbt.fall_speed for xbt in self.measurements] + initial_fall_speeds = [xbt.fall_speed for xbt in measurements] # XBT depth can not be too shallow, because kernel would break. for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False): if not max_depth <= -DT * fall_speed: raise ValueError( - f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}" + f"XBT max_depth or bathymetry shallower than minimum {-DT * fall_speed}. It is likely the XBT cannot be deployed in this area, which is too shallow." ) # define xbt particles xbt_particleset = ParticleSet( fieldset=fieldset, pclass=_XBTParticle, - lon=[xbt.spacetime.location.lon for xbt in self.measurements], - lat=[xbt.spacetime.location.lat for xbt in self.measurements], - depth=[xbt.min_depth for xbt in self.measurements], - time=[xbt.spacetime.time for xbt in self.measurements], + lon=[xbt.spacetime.location.lon for xbt in measurements], + lat=[xbt.spacetime.location.lat for xbt in measurements], + depth=[xbt.min_depth for xbt in measurements], + time=[xbt.spacetime.time for xbt in measurements], max_depth=max_depths, - min_depth=[xbt.min_depth for xbt in self.measurements], - fall_speed=[xbt.fall_speed for xbt in self.measurements], + min_depth=[xbt.min_depth for xbt in measurements], + fall_speed=[xbt.fall_speed for xbt in measurements], ) out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=OUTPUT_DT) From e5c08ce049be8440d7039cb2eb2e9af7f79ba59a Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:07:55 +0100 Subject: [PATCH 32/56] bug fixes --- src/virtualship/instruments/ctd_bgc.py | 14 +++++++------- src/virtualship/instruments/drifter.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 1b702b6e..f3174fd9 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -228,7 +228,7 @@ def simulate(self, measurements, out_path) -> None: time=0, ), ) - for ctd_bgc in self.measurements + for ctd_bgc in measurements ] # CTD depth can not be too shallow, because kernel would break. @@ -242,13 +242,13 @@ def simulate(self, measurements, out_path) -> None: ctd_bgc_particleset = ParticleSet( fieldset=fieldset, pclass=_CTD_BGCParticle, - lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in self.measurements], - lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in self.measurements], - depth=[ctd_bgc.min_depth for ctd_bgc in self.measurements], - time=[ctd_bgc.spacetime.time for ctd_bgc in self.measurements], + lon=[ctd_bgc.spacetime.location.lon for ctd_bgc in measurements], + lat=[ctd_bgc.spacetime.location.lat for ctd_bgc in measurements], + depth=[ctd_bgc.min_depth for ctd_bgc in measurements], + time=[ctd_bgc.spacetime.time for ctd_bgc in measurements], max_depth=max_depths, - min_depth=[ctd_bgc.min_depth for ctd_bgc in self.measurements], - winch_speed=[WINCH_SPEED for _ in self.measurements], + min_depth=[ctd_bgc.min_depth for ctd_bgc in measurements], + winch_speed=[WINCH_SPEED for _ in measurements], ) # define output file for the simulation diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 93cd2631..f33fde55 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -164,7 +164,7 @@ def simulate(self, measurements, out_path) -> None: # if there are more particles left than the number of drifters with an indefinite endtime, warn the user if len(drifter_particleset.particledata) > len( - [d for d in self.measurements if d.lifetime is None] + [d for d in measurements if d.lifetime is None] ): print( "WARN: Some drifters had a life time beyond the end time of the fieldset or the requested end time." From a4f8af0c88f65837eccd82fd420c7904cd8962f3 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 08:43:52 +0100 Subject: [PATCH 33/56] version U and V downloaded --- src/virtualship/instruments/base.py | 2 ++ src/virtualship/instruments/ctd.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index c45c71fe..23b324e5 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -245,6 +245,8 @@ def load_input_data(self) -> FieldSet: # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? # TODO: tests need updating...! + #! TODO: CTD, CTD_BGC and Underway_ST deployment testing in `run` is outstanding! + try: data_dir = self._get_data_dir(self.directory) joined_filepaths = { diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index ffc88922..86c6e0ac 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -10,6 +10,9 @@ from virtualship.models import Spacetime from virtualship.utils import register_input_dataset, register_instrument +# TODO: add some kind of check that each instrument has a dataclass, particle class, InputDataset class and Instrument class? +# TODO: probably as a test + @dataclass class CTD: @@ -33,6 +36,9 @@ class CTD: ) +# TODO: way to group kernels together, just to make clearer? + + def _sample_temperature(particle, fieldset, time): particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] @@ -104,6 +110,8 @@ class CTDInstrument(Instrument): def __init__(self, expedition, directory): """Initialize CTDInstrument.""" + #! TODO: actually don't need to download U and V for CTD simulation... can instead add mock/duplicate of T and name it U (also don't need V)! + filenames = { "U": f"{CTD.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? "V": f"{CTD.name}_uv.nc", From e3c57f628586b89344734b276ca121f668b1fcdd Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 10:30:09 +0100 Subject: [PATCH 34/56] dummy U and V --- src/virtualship/instruments/base.py | 19 +++++++------- src/virtualship/instruments/ctd.py | 14 ++++------- src/virtualship/instruments/ctd_bgc.py | 14 +++-------- .../instruments/ship_underwater_st.py | 15 +++++------ src/virtualship/utils.py | 25 +++++++++++++++++++ 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 23b324e5..6c036b28 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,11 +4,13 @@ import copernicusmarine import numpy as np +from yaspin import yaspin from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.errors import CopernicusCatalogueError from virtualship.models import Expedition, SpaceTimeRegion +from virtualship.utils import ship_spinner PRODUCT_IDS = { "phys": { @@ -244,9 +246,6 @@ def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? # TODO: tests need updating...! - - #! TODO: CTD, CTD_BGC and Underway_ST deployment testing in `run` is outstanding! - try: data_dir = self._get_data_dir(self.directory) joined_filepaths = { @@ -291,13 +290,13 @@ def run(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! - # with yaspin( - # text=f"Simulating {self.name} measurements... ", - # side="right", - # spinner=ship_spinner, - # ) as spinner: - self.simulate(measurements, out_path) - # spinner.ok("✅") + with yaspin( + text=f"Simulating {self.name} measurements... ", + side="right", + spinner=ship_spinner, + ) as spinner: + self.simulate(measurements, out_path) + spinner.ok("✅") def _get_data_dir(self, expedition_dir: Path) -> Path: space_time_region_hash = get_space_time_region_hash( diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 86c6e0ac..22c69799 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -8,7 +8,7 @@ from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument # TODO: add some kind of check that each instrument has a dataclass, particle class, InputDataset class and Instrument class? # TODO: probably as a test @@ -86,11 +86,6 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { - "UVdata": { - "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, "Sdata": { "physical": True, "variables": ["so"], @@ -113,12 +108,10 @@ def __init__(self, expedition, directory): #! TODO: actually don't need to download U and V for CTD simulation... can instead add mock/duplicate of T and name it U (also don't need V)! filenames = { - "U": f"{CTD.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "V": f"{CTD.name}_uv.nc", "S": f"{CTD.name}_s.nc", "T": f"{CTD.name}_t.nc", } - variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} + variables = {"S": "so", "T": "thetao"} super().__init__( CTD.name, @@ -145,6 +138,9 @@ def simulate(self, measurements, out_path) -> None: fieldset = self.load_input_data() + # add dummy U + add_dummy_UV(fieldset) # TODO: parcels v3 bodge; remove when parcels v4 is used + fieldset_starttime = fieldset.T.grid.time_origin.fulltime( fieldset.T.grid.time_full[0] ) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index f3174fd9..c4245823 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -8,7 +8,7 @@ from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument @dataclass @@ -105,11 +105,6 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Variable specific args for instrument.""" return { - "UVdata": { - "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, "o2data": { "physical": False, "variables": ["o2"], @@ -155,8 +150,6 @@ class CTD_BGCInstrument(Instrument): def __init__(self, expedition, directory): """Initialize CTD_BGCInstrument.""" filenames = { - "U": f"{CTD_BGC.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "V": f"{CTD_BGC.name}_uv.nc", "o2": f"{CTD_BGC.name}_o2.nc", "chl": f"{CTD_BGC.name}_chl.nc", "no3": f"{CTD_BGC.name}_no3.nc", @@ -166,8 +159,6 @@ def __init__(self, expedition, directory): "nppv": f"{CTD_BGC.name}_nppv.nc", } variables = { - "U": "uo", - "V": "vo", "o2": "o2", "chl": "chl", "no3": "no3", @@ -201,6 +192,9 @@ def simulate(self, measurements, out_path) -> None: fieldset = self.load_input_data() + # add dummy U + add_dummy_UV(fieldset) # TODO: parcels v3 bodge; remove when parcels v4 is used + fieldset_starttime = fieldset.o2.grid.time_origin.fulltime( fieldset.o2.grid.time_full[0] ) diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 2ffbd762..f2393778 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -6,7 +6,7 @@ from parcels import ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument @dataclass @@ -61,11 +61,6 @@ def __init__(self, data_dir, credentials, space_time_region): def get_datasets_dict(self) -> dict: """Get variable specific args for instrument.""" return { - "UVdata": { - "physical": True, # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, "Sdata": { "physical": True, "variables": ["so"], @@ -86,12 +81,10 @@ class Underwater_STInstrument(Instrument): def __init__(self, expedition, directory): """Initialize Underwater_STInstrument.""" filenames = { - "U": f"{Underwater_ST.name}_uv.nc", # TODO: U and V are only needed for parcels.FieldSet.check_complete()... would be nice to remove... v4? - "V": f"{Underwater_ST.name}_uv.nc", "S": f"{Underwater_ST.name}_s.nc", "T": f"{Underwater_ST.name}_t.nc", } - variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} + variables = {"S": "so", "T": "thetao"} super().__init__( Underwater_ST.name, @@ -110,6 +103,10 @@ def simulate(self, measurements, out_path) -> None: measurements.sort(key=lambda p: p.time) fieldset = self.load_input_data() + + # add dummy U + add_dummy_UV(fieldset) # TODO: parcels v3 bodge; remove when parcels v4 is used + particleset = ParticleSet.from_list( fieldset=fieldset, pclass=_ShipSTParticle, diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 6ffaefe7..324f8c67 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -8,6 +8,8 @@ from pathlib import Path from typing import TYPE_CHECKING, TextIO +from parcels import FieldSet + if TYPE_CHECKING: from virtualship.models import Expedition @@ -268,3 +270,26 @@ def get_input_dataset_class(instrument_type): def get_instrument_class(instrument_type): return INSTRUMENT_CLASS_MAP.get(instrument_type) + + +def add_dummy_UV(fieldset: FieldSet): + """Add a dummy U and V field to a FieldSet to satisfy parcels FieldSet completeness checks.""" + if "U" not in fieldset.__dict__.keys(): + for uv_var in ["U", "V"]: + dummy_field = getattr( + FieldSet.from_data( + {"U": 0, "V": 0}, {"lon": 0, "lat": 0}, mesh="spherical" + ), + uv_var, + ) + fieldset.add_field(dummy_field) + try: + fieldset.time_origin = ( + fieldset.T.grid.time_origin + if "T" in fieldset.__dict__.keys() + else fieldset.o2.grid.time_origin + ) + except Exception: + raise ValueError( + "Cannot determine time_origin for dummy UV fields. Assert T or o2 exists in fieldset." + ) from None From 813a245a159547db42cb8bce58f7e36e1ac55076 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:22:33 +0100 Subject: [PATCH 35/56] Neaten up logging output --- src/virtualship/expedition/do_expedition.py | 6 +++++ src/virtualship/instruments/adcp.py | 3 ++- src/virtualship/instruments/argo_float.py | 4 +++- src/virtualship/instruments/base.py | 22 ++++++++++++++----- src/virtualship/instruments/ctd.py | 3 ++- src/virtualship/instruments/ctd_bgc.py | 4 +++- src/virtualship/instruments/drifter.py | 3 ++- .../instruments/ship_underwater_st.py | 3 ++- src/virtualship/instruments/xbt.py | 3 ++- 9 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index ec61b1b0..b857b72c 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -1,5 +1,6 @@ """do_expedition function.""" +import logging import os import shutil from pathlib import Path @@ -26,6 +27,11 @@ projection = pyproj.Geod(ellps="WGS84") +# parcels logger (suppress INFO messages to prevent log being flooded) +external_logger = logging.getLogger("parcels.tools.loggers") +external_logger.setLevel(logging.WARNING) + + def do_expedition(expedition_dir: str | Path) -> None: """ Perform an expedition, providing terminal feedback and file output. diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index db5be8ef..20a12436 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -89,6 +89,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=False, allow_time_extrapolation=True, + verbose_progress=False, ) def simulate(self, measurements, out_path) -> None: @@ -127,6 +128,6 @@ def simulate(self, measurements, out_path) -> None: [_sample_velocity], dt=1, runtime=1, - verbose_progress=False, + verbose_progress=self.verbose_progress, output_file=out_file, ) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 93aa9093..898fd5cc 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -138,6 +138,7 @@ def __init__(self, data_dir, credentials, space_time_region): data_dir, credentials, space_time_region, + verbose_progress=True, ) def get_datasets_dict(self) -> dict: @@ -182,6 +183,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=False, allow_time_extrapolation=False, + verbose_progress=True, ) def simulate(self, measurements, out_path) -> None: @@ -243,5 +245,5 @@ def simulate(self, measurements, out_path) -> None: endtime=actual_endtime, dt=DT, output_file=out_file, - verbose_progress=True, + verbose_progress=self.verbose_progress, ) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 6c036b28..8212aab7 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -224,6 +224,7 @@ def __init__( variables: dict, add_bathymetry: bool, allow_time_extrapolation: bool, + verbose_progress: bool, bathymetry_file: str = "bathymetry.nc", ): """Initialise instrument.""" @@ -241,11 +242,16 @@ def __init__( self.bathymetry_file = bathymetry_file self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation + self.verbose_progress = verbose_progress def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? # TODO: tests need updating...! + + #! TODO: E.g. ADCP is giving too much depth data?! + #! TODO: in fact output from most instruments doesn't look quite right...? + try: data_dir = self._get_data_dir(self.directory) joined_filepaths = { @@ -290,13 +296,17 @@ def run(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! - with yaspin( - text=f"Simulating {self.name} measurements... ", - side="right", - spinner=ship_spinner, - ) as spinner: + if not self.verbose_progress: + with yaspin( + text=f"Simulating {self.name} measurements... ", + side="right", + spinner=ship_spinner, + ) as spinner: + self.simulate(measurements, out_path) + spinner.ok("✅") + else: + print(f"Simulating {self.name} measurements... ") self.simulate(measurements, out_path) - spinner.ok("✅") def _get_data_dir(self, expedition_dir: Path) -> Path: space_time_region_hash = get_space_time_region_hash( diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 22c69799..9b59a770 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -121,6 +121,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=True, allow_time_extrapolation=True, + verbose_progress=False, ) def simulate(self, measurements, out_path) -> None: @@ -199,7 +200,7 @@ def simulate(self, measurements, out_path) -> None: [_sample_salinity, _sample_temperature, _ctd_cast], endtime=fieldset_endtime, dt=DT, - verbose_progress=False, + verbose_progress=self.verbose_progress, output_file=out_file, ) diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index c4245823..397f1af4 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -100,6 +100,7 @@ def __init__(self, data_dir, credentials, space_time_region): data_dir, credentials, space_time_region, + verbose_progress=False, ) def get_datasets_dict(self) -> dict: @@ -175,6 +176,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=True, allow_time_extrapolation=True, + verbose_progress=False, ) def simulate(self, measurements, out_path) -> None: @@ -262,7 +264,7 @@ def simulate(self, measurements, out_path) -> None: ], endtime=fieldset_endtime, dt=DT, - verbose_progress=False, + verbose_progress=self.verbose_progress, output_file=out_file, ) diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index f33fde55..3b047fed 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -102,6 +102,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=False, allow_time_extrapolation=False, + verbose_progress=True, ) def simulate(self, measurements, out_path) -> None: @@ -159,7 +160,7 @@ def simulate(self, measurements, out_path) -> None: endtime=actual_endtime, dt=DT, output_file=out_file, - verbose_progress=True, + verbose_progress=self.verbose_progress, ) # if there are more particles left than the number of drifters with an indefinite endtime, warn the user diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index f2393778..b52887a2 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -94,6 +94,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=False, allow_time_extrapolation=True, + verbose_progress=False, ) def simulate(self, measurements, out_path) -> None: @@ -129,6 +130,6 @@ def simulate(self, measurements, out_path) -> None: [_sample_salinity, _sample_temperature], dt=1, runtime=1, - verbose_progress=False, + verbose_progress=self.verbose_progress, output_file=out_file, ) diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index b15c0e6f..641015f5 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -122,6 +122,7 @@ def __init__(self, expedition, directory): variables, add_bathymetry=True, allow_time_extrapolation=True, + verbose_progress=False, ) def simulate(self, measurements, out_path) -> None: @@ -193,7 +194,7 @@ def simulate(self, measurements, out_path) -> None: [_sample_temperature, _xbt_cast], endtime=fieldset_endtime, dt=DT, - verbose_progress=False, + verbose_progress=self.verbose_progress, output_file=out_file, ) From b94f4f0b1b7c43caf4d72e5f9762494b1366c42b Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:14:26 +0100 Subject: [PATCH 36/56] small bug fixes --- src/virtualship/instruments/argo_float.py | 1 - src/virtualship/instruments/ctd_bgc.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 898fd5cc..4a5dcdef 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -138,7 +138,6 @@ def __init__(self, data_dir, credentials, space_time_region): data_dir, credentials, space_time_region, - verbose_progress=True, ) def get_datasets_dict(self) -> dict: diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 397f1af4..537ba810 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -100,7 +100,6 @@ def __init__(self, data_dir, credentials, space_time_region): data_dir, credentials, space_time_region, - verbose_progress=False, ) def get_datasets_dict(self) -> dict: From a5d10e7d4ab9e417c195c460d41c7ba9944b1718 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:43:00 +0100 Subject: [PATCH 37/56] tidy up --- src/virtualship/instruments/adcp.py | 25 +++++++++++++++++-- src/virtualship/instruments/argo_float.py | 22 ++++++++++++++++ src/virtualship/instruments/base.py | 7 ++---- src/virtualship/instruments/ctd.py | 24 +++++++++++++++--- src/virtualship/instruments/ctd_bgc.py | 22 ++++++++++++++++ src/virtualship/instruments/drifter.py | 22 ++++++++++++++++ .../instruments/ship_underwater_st.py | 22 ++++++++++++++++ src/virtualship/instruments/xbt.py | 22 ++++++++++++++++ src/virtualship/models/expedition.py | 1 - 9 files changed, 156 insertions(+), 11 deletions(-) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 20a12436..b2ebe988 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -11,6 +11,10 @@ register_instrument, ) +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class ADCP: @@ -19,8 +23,11 @@ class ADCP: name: ClassVar[str] = "ADCP" -# we specifically use ScipyParticle because we have many small calls to execute -# there is some overhead with JITParticle and this ends up being significantly faster +# ===================================================== +# SECTION: Particle Class +# ===================================================== + + _ADCPParticle = ScipyParticle.add_variables( [ Variable("U", dtype=np.float32, initial=np.nan), @@ -28,6 +35,10 @@ class ADCP: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + def _sample_velocity(particle, fieldset, time): particle.U, particle.V = fieldset.UV.eval( @@ -35,6 +46,11 @@ def _sample_velocity(particle, fieldset, time): ) +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.ADCP) class ADCPInputDataset(InputDataset): """Input dataset for ADCP instrument.""" @@ -70,6 +86,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.ADCP) class ADCPInstrument(Instrument): """ADCP instrument class.""" diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 4a5dcdef..573836b4 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -17,6 +17,10 @@ from virtualship.models.spacetime import Spacetime from virtualship.utils import register_input_dataset, register_instrument +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class ArgoFloat: @@ -32,6 +36,10 @@ class ArgoFloat: drift_days: float +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _ArgoParticle = JITParticle.add_variables( [ Variable("cycle_phase", dtype=np.int32, initial=0.0), @@ -48,6 +56,10 @@ class ArgoFloat: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + def _argo_float_vertical_movement(particle, fieldset, time): if particle.cycle_phase == 0: @@ -116,6 +128,11 @@ def _check_error(particle, fieldset, time): particle.delete() +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.ARGO_FLOAT) class ArgoFloatInputDataset(InputDataset): """Input dataset for ArgoFloat instrument.""" @@ -161,6 +178,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.ARGO_FLOAT) class ArgoFloatInstrument(Instrument): """ArgoFloat instrument class.""" diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 8212aab7..3c6a333b 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -246,12 +246,8 @@ def __init__( def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" - # TODO: can simulate_schedule.py be refactored to be contained in base.py and repsective instrument files too...? # TODO: tests need updating...! - #! TODO: E.g. ADCP is giving too much depth data?! - #! TODO: in fact output from most instruments doesn't look quite right...? - try: data_dir = self._get_data_dir(self.directory) joined_filepaths = { @@ -303,10 +299,11 @@ def run(self, measurements: list, out_path: str | Path) -> None: spinner=ship_spinner, ) as spinner: self.simulate(measurements, out_path) - spinner.ok("✅") + spinner.ok("✅\n") else: print(f"Simulating {self.name} measurements... ") self.simulate(measurements, out_path) + print("\n") def _get_data_dir(self, expedition_dir: Path) -> Path: space_time_region_hash = get_space_time_region_hash( diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 9b59a770..5080c006 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -13,6 +13,10 @@ # TODO: add some kind of check that each instrument has a dataclass, particle class, InputDataset class and Instrument class? # TODO: probably as a test +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class CTD: @@ -24,6 +28,10 @@ class CTD: max_depth: float +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _CTDParticle = JITParticle.add_variables( [ Variable("salinity", dtype=np.float32, initial=np.nan), @@ -36,7 +44,9 @@ class CTD: ) -# TODO: way to group kernels together, just to make clearer? +# ===================================================== +# SECTION: Kernels +# ===================================================== def _sample_temperature(particle, fieldset, time): @@ -61,6 +71,11 @@ def _ctd_cast(particle, fieldset, time): particle.delete() +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.CTD) class CTDInputDataset(InputDataset): """Input dataset for CTD instrument.""" @@ -99,14 +114,17 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.CTD) class CTDInstrument(Instrument): """CTD instrument class.""" def __init__(self, expedition, directory): """Initialize CTDInstrument.""" - #! TODO: actually don't need to download U and V for CTD simulation... can instead add mock/duplicate of T and name it U (also don't need V)! - filenames = { "S": f"{CTD.name}_s.nc", "T": f"{CTD.name}_t.nc", diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 537ba810..28dd55c3 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -10,6 +10,10 @@ from virtualship.models.spacetime import Spacetime from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class CTD_BGC: @@ -21,6 +25,10 @@ class CTD_BGC: max_depth: float +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _CTD_BGCParticle = JITParticle.add_variables( [ Variable("o2", dtype=np.float32, initial=np.nan), @@ -37,6 +45,10 @@ class CTD_BGC: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + def _sample_o2(particle, fieldset, time): particle.o2 = fieldset.o2[time, particle.depth, particle.lat, particle.lon] @@ -80,6 +92,11 @@ def _ctd_bgc_cast(particle, fieldset, time): particle.delete() +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.CTD_BGC) class CTD_BGCInputDataset(InputDataset): """Input dataset object for CTD_BGC instrument.""" @@ -143,6 +160,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.CTD_BGC) class CTD_BGCInstrument(Instrument): """CTD_BGC instrument class.""" diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 3b047fed..0ce78624 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -10,6 +10,10 @@ from virtualship.models.spacetime import Spacetime from virtualship.utils import register_input_dataset, register_instrument +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class Drifter: @@ -21,6 +25,10 @@ class Drifter: lifetime: timedelta | None # if none, lifetime is infinite +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _DrifterParticle = JITParticle.add_variables( [ Variable("temperature", dtype=np.float32, initial=np.nan), @@ -30,6 +38,10 @@ class Drifter: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + def _sample_temperature(particle, fieldset, time): particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] @@ -42,6 +54,11 @@ def _check_lifetime(particle, fieldset, time): particle.delete() +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.DRIFTER) class DrifterInputDataset(InputDataset): """Input dataset for Drifter instrument.""" @@ -82,6 +99,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.DRIFTER) class DrifterInstrument(Instrument): """Drifter instrument class.""" diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index b52887a2..946e1a9b 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -8,6 +8,10 @@ from virtualship.instruments.types import InstrumentType from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class Underwater_ST: @@ -16,6 +20,10 @@ class Underwater_ST: name: ClassVar[str] = "Underwater_ST" +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _ShipSTParticle = ScipyParticle.add_variables( [ Variable("S", dtype=np.float32, initial=np.nan), @@ -23,6 +31,10 @@ class Underwater_ST: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + # define function sampling Salinity def _sample_salinity(particle, fieldset, time): @@ -34,6 +46,11 @@ def _sample_temperature(particle, fieldset, time): particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.UNDERWATER_ST) class Underwater_STInputDataset(InputDataset): """Input dataset for Underwater_ST instrument.""" @@ -74,6 +91,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.UNDERWATER_ST) class Underwater_STInstrument(Instrument): """Underwater_ST instrument class.""" diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 641015f5..98378c98 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -10,6 +10,10 @@ from virtualship.models.spacetime import Spacetime from virtualship.utils import register_input_dataset, register_instrument +# ===================================================== +# SECTION: Dataclass +# ===================================================== + @dataclass class XBT: @@ -23,6 +27,10 @@ class XBT: deceleration_coefficient: float +# ===================================================== +# SECTION: Particle Class +# ===================================================== + _XBTParticle = JITParticle.add_variables( [ Variable("temperature", dtype=np.float32, initial=np.nan), @@ -33,6 +41,10 @@ class XBT: ] ) +# ===================================================== +# SECTION: Kernels +# ===================================================== + def _sample_temperature(particle, fieldset, time): particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon] @@ -56,6 +68,11 @@ def _xbt_cast(particle, fieldset, time): particle_ddepth = particle.max_depth - particle.depth +# ===================================================== +# SECTION: InputDataset Class +# ===================================================== + + @register_input_dataset(InstrumentType.XBT) class XBTInputDataset(InputDataset): """Input dataset for XBT instrument.""" @@ -101,6 +118,11 @@ def get_datasets_dict(self) -> dict: } +# ===================================================== +# SECTION: Instrument Class +# ===================================================== + + @register_instrument(InstrumentType.XBT) class XBTInstrument(Instrument): """XBT instrument class.""" diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index c70de855..54021176 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -341,7 +341,6 @@ class XBTConfig(pydantic.BaseModel): class InstrumentsConfig(pydantic.BaseModel): - # TODO: refactor potential for this? Move explicit instrument_config's away from models/ dir? """Configuration of instruments.""" argo_float_config: ArgoFloatConfig | None = None From 37530329840f87ad08e1e7c0bd067a7fdcbc9515 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:50:10 +0000 Subject: [PATCH 38/56] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/virtualship/instruments/adcp.py | 2 +- src/virtualship/instruments/argo_float.py | 2 +- src/virtualship/instruments/base.py | 2 +- src/virtualship/instruments/ctd.py | 2 +- src/virtualship/instruments/ctd_bgc.py | 2 +- src/virtualship/instruments/drifter.py | 2 +- src/virtualship/instruments/ship_underwater_st.py | 2 +- src/virtualship/instruments/xbt.py | 2 +- src/virtualship/models/expedition.py | 2 +- tests/instruments/test_ctd.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index b2ebe988..857e9655 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np - from parcels import ParticleSet, ScipyParticle, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import ( diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 573836b4..64961605 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -4,7 +4,6 @@ from typing import ClassVar import numpy as np - from parcels import ( AdvectionRK4, JITParticle, @@ -12,6 +11,7 @@ StatusCode, Variable, ) + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 3c6a333b..93ff8ed8 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,9 +4,9 @@ import copernicusmarine import numpy as np +from parcels import Field, FieldSet from yaspin import yaspin -from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.errors import CopernicusCatalogueError from virtualship.models import Expedition, SpaceTimeRegion diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 5080c006..2c6dc56e 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models import Spacetime diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 28dd55c3..23c978f7 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 0ce78624..f854d51f 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 946e1a9b..161bb184 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np - from parcels import ParticleSet, ScipyParticle, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 98378c98..68502533 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 54021176..d6727f69 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -9,8 +9,8 @@ import pydantic import pyproj import yaml - from parcels import Field + from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.types import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta diff --git a/tests/instruments/test_ctd.py b/tests/instruments/test_ctd.py index 0a8edcfa..14e0a276 100644 --- a/tests/instruments/test_ctd.py +++ b/tests/instruments/test_ctd.py @@ -9,8 +9,8 @@ import numpy as np import xarray as xr - from parcels import Field, FieldSet + from virtualship.instruments.ctd import CTD, simulate_ctd from virtualship.models import Location, Spacetime From b7904e8995cdeacf53538623e5efdf51d7268f8e Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:38:10 +0100 Subject: [PATCH 39/56] Refactor type hints and improve test coverage for instruments and utils --- src/virtualship/instruments/base.py | 41 +++++++------ src/virtualship/instruments/ctd.py | 8 ++- tests/cli/test_fetch.py | 27 --------- tests/instruments/test_base.py | 92 +++++++++++++++++++++-------- tests/test_utils.py | 18 +++++- 5 files changed, 113 insertions(+), 73 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 3c6a333b..0bb77c33 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -1,6 +1,7 @@ import abc from datetime import timedelta from pathlib import Path +from typing import TYPE_CHECKING import copernicusmarine import numpy as np @@ -9,9 +10,11 @@ from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.errors import CopernicusCatalogueError -from virtualship.models import Expedition, SpaceTimeRegion from virtualship.utils import ship_spinner +if TYPE_CHECKING: + from virtualship.models import Expedition, SpaceTimeRegion + PRODUCT_IDS = { "phys": { "reanalysis": "cmems_mod_glo_phy_my_0.083deg_P1D-m", @@ -60,7 +63,7 @@ def __init__( max_depth: float, data_dir: str, credentials: dict, - space_time_region: SpaceTimeRegion, + space_time_region: "SpaceTimeRegion", ): """Initialise input dataset.""" self.name = name @@ -185,22 +188,7 @@ def _select_product_id( "No suitable product found in the Copernicus Marine Catalogue for the scheduled time and variable." ) - def start_end_in_product_timerange( - selected_id, schedule_start, schedule_end, username, password - ): - ds_selected = copernicusmarine.open_dataset( - selected_id, username=username, password=password - ) - time_values = ds_selected["time"].values - import numpy as np - - time_min, time_max = np.min(time_values), np.max(time_values) - return ( - np.datetime64(schedule_start) >= time_min - and np.datetime64(schedule_end) <= time_max - ) - - if start_end_in_product_timerange( + if self._start_end_in_product_timerange( selected_id, schedule_start, schedule_end, username, password ): return selected_id @@ -211,6 +199,21 @@ def start_end_in_product_timerange( else BGC_ANALYSIS_IDS[variable] ) + def _start_end_in_product_timerange( + self, selected_id, schedule_start, schedule_end, username, password + ): + ds_selected = copernicusmarine.open_dataset( + selected_id, username=username, password=password + ) + time_values = ds_selected["time"].values + import numpy as np + + time_min, time_max = np.min(time_values), np.max(time_values) + return ( + np.datetime64(schedule_start) >= time_min + and np.datetime64(schedule_end) <= time_max + ) + class Instrument(abc.ABC): """Base class for instruments and their simulation.""" @@ -218,7 +221,7 @@ class Instrument(abc.ABC): def __init__( self, name: str, - expedition: Expedition, + expedition: "Expedition", directory: Path | str, filenames: dict, variables: dict, diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 5080c006..8e524f4a 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -1,13 +1,15 @@ from dataclasses import dataclass from datetime import timedelta -from typing import ClassVar +from typing import TYPE_CHECKING, ClassVar import numpy as np from parcels import JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType -from virtualship.models import Spacetime + +if TYPE_CHECKING: + from virtualship.models.spacetime import Spacetime from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument # TODO: add some kind of check that each instrument has a dataclass, particle class, InputDataset class and Instrument class? @@ -23,7 +25,7 @@ class CTD: """CTD configuration.""" name: ClassVar[str] = "CTD" - spacetime: Spacetime + spacetime: "Spacetime" min_depth: float max_depth: float diff --git a/tests/cli/test_fetch.py b/tests/cli/test_fetch.py index 9725601a..4c041dbe 100644 --- a/tests/cli/test_fetch.py +++ b/tests/cli/test_fetch.py @@ -17,8 +17,6 @@ get_existing_download, hash_model, hash_to_filename, - select_product_id, - start_end_in_product_timerange, ) from virtualship.models import Expedition from virtualship.utils import EXPEDITION, get_example_expedition @@ -98,31 +96,6 @@ def test_complete_download(tmp_path): assert_complete_download(tmp_path) -@pytest.mark.usefixtures("copernicus_no_download") -def test_select_product_id(expedition): - """Should return the physical reanalysis product id via the timings prescribed in the static schedule.yaml file.""" - result = select_product_id( - physical=True, - schedule_start=expedition.schedule.space_time_region.time_range.start_time, - schedule_end=expedition.schedule.space_time_region.time_range.end_time, - username="test", - password="test", - ) - assert result == "cmems_mod_glo_phy_my_0.083deg_P1D-m" - - -@pytest.mark.usefixtures("copernicus_no_download") -def test_start_end_in_product_timerange(expedition): - """Should return True for valid range ass determined by the static schedule.yaml file.""" - assert start_end_in_product_timerange( - selected_id="cmems_mod_glo_phy_my_0.083deg_P1D-m", - schedule_start=expedition.schedule.space_time_region.time_range.start_time, - schedule_end=expedition.schedule.space_time_region.time_range.end_time, - username="test", - password="test", - ) - - def test_assert_complete_download_complete(tmp_path): # Setup DownloadMetadata(download_complete=True).to_yaml(tmp_path / DOWNLOAD_METADATA) diff --git a/tests/instruments/test_base.py b/tests/instruments/test_base.py index f4192092..65e4ae0e 100644 --- a/tests/instruments/test_base.py +++ b/tests/instruments/test_base.py @@ -1,9 +1,12 @@ import datetime -from unittest.mock import patch +from pathlib import Path +from unittest.mock import MagicMock, patch +import numpy as np import pytest +import xarray as xr -from virtualship.instruments.base import InputDataset +from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.space_time_region import ( SpaceTimeRegion, @@ -12,6 +15,18 @@ ) from virtualship.utils import get_input_dataset_class +# test dataclass, particle class, kernels, etc. are defined for each instrument + + +# TODO: add all the other things here like particle class, kernels, etc. +def test_all_instruments_have_input_class(): + for instrument in InstrumentType: + input_class = get_input_dataset_class(instrument) + assert input_class is not None, f"No input_class for {instrument}" + + +# test InputDataset class + class DummyInputDataset(InputDataset): """A minimal InputDataset subclass for testing purposes.""" @@ -20,7 +35,7 @@ def get_datasets_dict(self): """Return a dummy datasets dict for testing.""" return { "dummy": { - "dataset_id": "test_id", + "physical": True, "variables": ["var1"], "output_filename": "dummy.nc", } @@ -48,21 +63,6 @@ def dummy_space_time_region(): ) -def test_inputdataset_abstract_instantiation(): - # instantiation should not be allowed - with pytest.raises(TypeError): - InputDataset( - name="test", - latlon_buffer=0, - datetime_buffer=0, - min_depth=0, - max_depth=10, - data_dir=".", - credentials={"username": "u", "password": "p"}, - space_time_region=None, - ) - - def test_dummyinputdataset_initialization(dummy_space_time_region): ds = DummyInputDataset( name="test", @@ -83,8 +83,23 @@ def test_dummyinputdataset_initialization(dummy_space_time_region): assert ds.credentials["username"] == "u" +@patch("virtualship.instruments.base.copernicusmarine.open_dataset") @patch("virtualship.instruments.base.copernicusmarine.subset") -def test_download_data_calls_subset(mock_subset, dummy_space_time_region): +def test_download_data_calls_subset( + mock_subset, mock_open_dataset, dummy_space_time_region +): + """Test that download_data calls the subset function correctly, will also test Copernicus Marine product id search logic.""" + mock_open_dataset.return_value = xr.Dataset( + { + "time": ( + "time", + [ + np.datetime64("1993-01-01T00:00:00"), + np.datetime64("2023-01-01T01:00:00"), + ], + ) + } + ) ds = DummyInputDataset( name="test", latlon_buffer=0.5, @@ -99,7 +114,38 @@ def test_download_data_calls_subset(mock_subset, dummy_space_time_region): assert mock_subset.called -def test_all_instruments_have_input_class(): - for instrument in InstrumentType: - input_class = get_input_dataset_class(instrument) - assert input_class is not None, f"No input_class for {instrument}" +# test Instrument class + + +class DummyInstrument(Instrument): + """Minimal concrete Instrument for testing.""" + + def simulate(self, data_dir, measurements, out_path): + """Dummy simulate implementation for test.""" + self.simulate_called = True + + +@patch("virtualship.instruments.base.FieldSet") +@patch("virtualship.instruments.base.get_existing_download") +@patch("virtualship.instruments.base.get_space_time_region_hash") +def test_load_input_data_calls(mock_hash, mock_get_download, mock_FieldSet): + """Test Instrument.load_input_data with mocks.""" + mock_hash.return_value = "hash" + mock_get_download.return_value = Path("/tmp/data") + mock_fieldset = MagicMock() + mock_FieldSet.from_netcdf.return_value = mock_fieldset + mock_fieldset.gridset.grids = [MagicMock(negate_depth=MagicMock())] + mock_fieldset.__getitem__.side_effect = lambda k: MagicMock() + dummy = DummyInstrument( + name="test", + expedition=MagicMock(schedule=MagicMock(space_time_region=MagicMock())), + directory="/tmp", + filenames={"A": "a.nc"}, + variables={"A": "a"}, + add_bathymetry=False, + allow_time_extrapolation=False, + verbose_progress=False, + ) + fieldset = dummy.load_input_data() + assert mock_FieldSet.from_netcdf.called + assert fieldset == mock_fieldset diff --git a/tests/test_utils.py b/tests/test_utils.py index 0dcebd79..bb8208f6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,4 @@ -from virtualship.models import Expedition +from virtualship.models.expedition import Expedition from virtualship.utils import get_example_expedition @@ -12,3 +12,19 @@ def test_valid_example_expedition(tmp_path): file.write(get_example_expedition()) Expedition.from_yaml(path) + + +def test_instrument_registry_updates(): + from virtualship import utils + + class DummyInputDataset: + pass + + class DummyInstrument: + pass + + utils.register_input_dataset("DUMMY_TYPE")(DummyInputDataset) + utils.register_instrument("DUMMY_TYPE")(DummyInstrument) + + assert utils.INPUT_DATASET_MAP["DUMMY_TYPE"] is DummyInputDataset + assert utils.INSTRUMENT_CLASS_MAP["DUMMY_TYPE"] is DummyInstrument From 7d1c575c7b8bc309db7e6e147357ab83a6c19f7a Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:05:31 +0100 Subject: [PATCH 40/56] Remove TODO comments and tidy up imports in test files --- src/virtualship/instruments/ctd.py | 3 --- src/virtualship/models/expedition.py | 1 - tests/instruments/test_base.py | 1 - 3 files changed, 5 deletions(-) diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 8e524f4a..0164ef94 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -12,9 +12,6 @@ from virtualship.models.spacetime import Spacetime from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument -# TODO: add some kind of check that each instrument has a dataclass, particle class, InputDataset class and Instrument class? -# TODO: probably as a test - # ===================================================== # SECTION: Dataclass # ===================================================== diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 54021176..dc198f51 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -131,7 +131,6 @@ def verify( ) # check if all waypoints are in water using bathymetry data - # TODO: tests should be updated to check this! # TODO: write test that checks that will flag when waypoint is on land!! [add to existing suite of fail .verify() tests in test_expedition.py] # TODO: need to do an overhaul of the DATA which is in tests/expedition/expedition_dir - don't think it's currently suitable! land_waypoints = [] diff --git a/tests/instruments/test_base.py b/tests/instruments/test_base.py index 65e4ae0e..0a58efcd 100644 --- a/tests/instruments/test_base.py +++ b/tests/instruments/test_base.py @@ -18,7 +18,6 @@ # test dataclass, particle class, kernels, etc. are defined for each instrument -# TODO: add all the other things here like particle class, kernels, etc. def test_all_instruments_have_input_class(): for instrument in InstrumentType: input_class = get_input_dataset_class(instrument) From e74a5ada099226c60704b991af42397e1e5bd634 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:26:15 +0100 Subject: [PATCH 41/56] Refactor bathymetry error handling, update verification methods and remove unused function --- src/virtualship/models/expedition.py | 40 ++++++++-------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 7c109c99..af85c1c5 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -5,12 +5,11 @@ from pathlib import Path from typing import TYPE_CHECKING -import numpy as np import pydantic import pyproj import yaml -from parcels import Field +from parcels import Field from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.types import InstrumentType from virtualship.utils import _validate_numeric_mins_to_timedelta @@ -19,7 +18,7 @@ from .space_time_region import SpaceTimeRegion if TYPE_CHECKING: - from parcels import FieldSet + pass projection: pyproj.Geod = pyproj.Geod(ellps="WGS84") @@ -132,7 +131,6 @@ def verify( # check if all waypoints are in water using bathymetry data # TODO: write test that checks that will flag when waypoint is on land!! [add to existing suite of fail .verify() tests in test_expedition.py] - # TODO: need to do an overhaul of the DATA which is in tests/expedition/expedition_dir - don't think it's currently suitable! land_waypoints = [] if data_dir is not None: bathymetry_path = data_dir.joinpath("bathymetry.nc") @@ -147,18 +145,19 @@ def verify( f"Problem loading bathymetry data (used to verify waypoints are in water): {e}" ) from e for wp_i, wp in enumerate(self.waypoints): - bathy = bathymetry_field.eval( - 0, # time - 0, # depth (surface) - wp.location.lat, - wp.location.lon, - ) - if np.isnan(bathy) or bathy <= 0: + try: + bathymetry_field.eval( + 0, # time + 0, # depth (surface) + wp.location.lat, + wp.location.lon, + ) + except Exception: land_waypoints.append((wp_i, wp)) if len(land_waypoints) > 0: raise ScheduleError( - f"The following waypoints are on land: {['#' + str(wp_i + 1) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}" + f"The following waypoint(s) throw(s) error(s): {['#' + str(wp_i + 1) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}\n\nINFO: They are likely on land (bathymetry data cannot be interpolated to their location(s)).\n" ) elif not ignore_missing_bathymetry: raise ScheduleError( @@ -431,20 +430,3 @@ def verify(self, expedition: Expedition) -> None: raise InstrumentsConfigError( f"Expedition includes instrument '{inst_type.value}', but instruments_config does not provide configuration for it." ) - - -def _is_on_land_zero_uv(fieldset: FieldSet, waypoint: Waypoint) -> bool: - """ - Check if waypoint is on land by assuming zero velocity means land. - - :param fieldset: The fieldset to sample the velocity from. - :param waypoint: The waypoint to check. - :returns: If the waypoint is on land. - """ - return fieldset.UV.eval( - 0, - fieldset.gridset.grids[0].depth[0], - waypoint.location.lat, - waypoint.location.lon, - applyConversion=False, - ) == (0.0, 0.0) From dc638c60a313eda82c9d6780a74f91cf48ad27b7 Mon Sep 17 00:00:00 2001 From: Emma Daniels <135691556+ammedd@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:58:05 +0100 Subject: [PATCH 42/56] add code of conduct to assignments (#182) * add code of conduct to assignments * fix thumbnails --- docs/conf.py | 3 +- .../_images/freepik_code_of_conduct.jpg | Bin 0 -> 403789 bytes .../_images/freepik_research_vessel.jpg | Bin 0 -> 353099 bytes docs/user-guide/_images/vessel.jpg | Bin 11260 -> 0 bytes .../assignments/Code_of_conduct.ipynb | 104 ++++++++++++++++++ .../assignments/Sail_the_ship.ipynb | 7 ++ docs/user-guide/assignments/index.md | 2 +- 7 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 docs/user-guide/_images/freepik_code_of_conduct.jpg create mode 100644 docs/user-guide/_images/freepik_research_vessel.jpg delete mode 100644 docs/user-guide/_images/vessel.jpg create mode 100644 docs/user-guide/assignments/Code_of_conduct.ipynb diff --git a/docs/conf.py b/docs/conf.py index 2460915d..91cf56f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -75,6 +75,7 @@ "user-guide/assignments/Research_Proposal_only": "user-guide/_images/MFP.jpg", "user-guide/assignments/Virtualship_research_proposal": "user-guide/_images/AnnaWeber.jpeg", "user-guide/assignments/sciencecommunication_assignment": "user-guide/_images/marine_ss.jpg", - "user-guide/assignments/Sail_the_ship": "user-guide/_images/vessel.jpg", + "user-guide/assignments/Sail_the_ship": "user-guide/_images/freepik_research_vessel.jpg", + "user-guide/assignments/Code_of_conduct": "user-guide/_images/freepik_code_of_conduct.jpg", } nbsphinx_execute = "never" diff --git a/docs/user-guide/_images/freepik_code_of_conduct.jpg b/docs/user-guide/_images/freepik_code_of_conduct.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae1707bd4364cf3724d78223e4f70dcaf8e05c39 GIT binary patch literal 403789 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

C8Y`3sjFcd&-)`EyU1j|-d zA#VVAk?Q;1grvkDRLNFi(Fef^WjdSzG%3NAl+#QT+_WQ92BtTOv8q7l7piWu$xm!x z4B@)!Sa8-3zP`8KH9JDXsD)(mGe}%P6B~gc`x`7^CWKCeGs$l`k)5u34sf>S!z9!l zh8OP~b?>5?pkuHQ-p8VcU=mG*Zl8uFQnj@^KO*^_JOHeG3+%PqvOZM7V0q z_m9IXhxJKVeA>f$@=8^F_!&I?1eu2r0t;QBGY%{bW#_Sym%(lD;qbhT*$Iz|eX`$N z__f+jKY(@i?#^#opxKJ{s3pgQCi%?$c9GdUTHaC3(`erF(U=txbBk6-TiuSy>DvQe zTRSH3RJoX3YWldTI_-%C!NSka)swc|qhVbZ<5z2^&<4FM$iPyfDloar3T0yrT{Qu2 z4a5G|({4j9F6ksg`BUEjzPEkx}5bQ zlntm$D46b;0es7%;~JWd@AO2alA6#j!jGKg3$|g(m5K3UbIBRUqlFv9Nm@DSn7P^ufDGFr81oX;%F;kVv8rYy9>Q1rW5I zvXwtlkzjrImtTJYokz>b#Wk?%5xZ62+b|YuP7JhT6`_3bv-+9m^mw*psH;xUy`8_} zOdRPG8K9-eivQPuIbP?XOeX zOQWH-C+Q)BPY)K6iEw@*YO*8bA;X z6KT`8LmO3tGa4XXNfe=rt7^jK{^QW%*&RPSo$IPQ1}MtZh{ioIouyh)WcHep(_UbA3hf&rw-_}juiX5R*$gjKy zJUfB;3zXt7GGQTJ3f|=67`@4E52Qo7JnNSm4!5qPaQfh5RJt(RXa`55!jMmG(cx)e z=LosK_fb@NHmc$Uy<>51R4ugAnjI05Mj0>_y9h82DABXVPOcijSH}_`)ip+o0bIZf zSlGWUCYrT4K`zfE{Zs#)a74fr5>oTr>2+{wO+zkg-^m}m_(KV)*!3y!b|4FUcmYq1?>U$&$Z<+B#2vox~W+M!4W0HP%^mKhfZg_a9Ejwy*{{hiuFYUKBPF8 z;QCwUF%AfyIvmKJnn$AF-uoNxPG$LRBU4D+EK7Cyk!FcgQ*6AY+-Zgcj6z^ftI(n^ zlPoFL`SGA?QioNXu%47@KoK_Cc#dl}PyhYHKWcxbK?=RU^|)(tZO$)j$Ic3J0{Gb5M?MFLP23}}!)+s=`UqQXgF&&`Lnt-YA-%wBtNl&i_KF0J+a~|Em zu{rq$Mn(~$HU@<44|_`nY)<2m{Nr6;H!oIKJ4ArkM`yjn%Q%2l|N*?ENFOsQQ&b-S1VEQOf)X_<%?mTRfYTQw;0=t z-IiB9*kh<#{S8WQj?_$=c{Z~C)pJDmiSEe*c&FXrhLCL?U-t2ev!R3tCbB&k?d{dK zH#99$2809@0YVRXU=bM8QaBFeei2a$k6NS*DI3-$8T^o>y?oPwbm3z6cpH!?1kr4eJPHzPU)~WIe$)CI%M<`0u0ZOdu3=> z*1p{upW)6Dik(|ikohUS=pFd0Z+Bi6z-KDyngugN>GG1E^4GC?U5{AW@>jMooP-5` ziqP5y3(g^C42;@64Pl?5Z0HNdOf&EJ6k|LK*nOkR=e=;8ld^`!*8y?&(xwk0Ed4)J z5g9CneSf-q2z3EWxDKrpQ-nu!nsXnMmu9T}O4~mG8IoL}2%E|yDdak$zL2l{x7g++oMZ6y z%_T4v2X7(fK72Vx_g7S8nsGWbA+7Lo5|FNuk*#Q9n6yfu=M5&Nq@L?u<>#nmyHt%1 zc_XKFj9Cfs---Pk$*jd2jE_1Fm6k-b`usz&)2=n*p?gL*)%WHnL^nJyb~;aAm?_fw zP*mSIn2%5lia%P^X~sSrOlo8lD4?QzkuB&Z z_wjv)$sM{mhh!4XjkN$1yj%my>b51I)&*v_Zke>MLrU$w#7THnmHpJ3HvvX6Z#(Pe z@qM^lO4`!s4R>ue%-T{`3&9h}im4NODZ-thd(K6KZ5dJYY|9DL#Is32c{>&=b!?kE zG5#+QXZj+h4>Bu_f^&!~U%3=9`!5_fais+(k6@l12L$2Rb(O2Yk{6XHP@9Xpa)!9_I`S^F2wU+wZ!+CjXj( ziZlBNu;^`OP!rsJN7qt0)P;>~{s@=Xq9Vwe4Z^(s@u zEB;p)8BeZ)rwP=TnjEL+Fn+$@0PEWB9=WosB&^b}i_J~6_X{GbVD8Q;Gqz%<1`yt^ zo*`KzkejS zf_{nVVIZkfY%67`(*V^(_oZ7t5}& zT^zMu%5+x>2G%T}Pm=+EAIwt)&n_R-q5vG0KcLc8P|zmoif3<>5$XK4iy*0>u~j8} zafad|Yg8&la7X5;r3=OIRv22TOR%Eu2SYYImSg>Erqvjp;4wBPgQBBkSbwVfxg*HD zq56XGNP=%R%_YNF@RrDuaEDDyU5rgPMa$F_T5tj6Y)7kApcVg8c#0@eyKf%Cue6Z&FiIKLv3 zVQ5jcc+Hd=9L5o-m{9C{i9eY*$2HboCSYn@w%mGP`%Zf8=kWa?;z9~(&&pQT`Ko!u zYTI(g76N}l;1925I0Qza5(|8JTZLb1HSDmu`Z6dfrO$mx>}h^OuYKd8(^0vVQ(W)V zQ8#QRy{0t)S8W%EZr%2lsTZ=d0h0TWkS!bnzic$0Zqo9snh934wyW}b{<#Y+ zV4S9@8a@O8>I6uZHAcnsAI8(I)TYBV4kwiVsNsInHyrQ+Zw=Y_PQJnG3vg2D(Nqjk za7{dp^-eKeC#}DRGza=Ln;`ueZ8?5N9M!IX;^mlTkpTOIYv`eg;ZJ^-&waUV6BMDN zLU?cbo^tP)HACaoNzno>!6I!1eS?YS?MMlbAbJ!gBe~D}j<}qQw{-=XY+z;z5z(2H zpl4e>G`Rut1EZ0?`ncy2e3t2JaU+ldL3Z|wL){oianZGpy$UT}k+TDPh_jFk<+y&P zOFUqXZeF5I)Dx=#7<~S(*yP8Q1ptBFMC4b??qv#ia2LK_{6Qag1_SV_tk($Daqg2< z&{~6#$QJ>+3srL^$WLs6JR5<<6S3XK%rUH2v^LAPp}EX7VG0E$JQ*cB0QrHnw};3( zN@C*KS{QIWy6X;*3(}A{3yD{{0(~F+8h#c2 zConD7#}}?o!5QJN&IpPVGuAde5@FvBS4ElFe{l4;+^*hnGd$3Ts223iK30sU`~P`Y z^E2Ar34)E2=&o&K4(kTIdyHNJd?l0Z%?XOr&Fps6+mdOTbv-p<4=k0HnNT1}f87mL ziis*4wv^T%U6FWH4PgvP=|^+mFOykiu5e8P1>_{V)a!u(l~Zx*@5{Lv9Z|eajcYi} zqeA+=)Wn_{{yz+A$FFFT*&#QSmitbK_l$ymTw)Eoh?hK73C!qgo>kwQFtRS+z~!4FJ|wmNQr9Ska>ZC-+fz` zvp^WkwcnP8+dUioC%H;tzT}*}y*lGCwitg3t+gfxZ>_u%AgABc*&w#!@`Y zz|h@V%nt_@cbkj74ZR;XpAcyw_zE#Q2>63K!!`N!$i5u2U@Q z#QA+|EF2Hf36)Pc=%s7wr#JReJ=8-?VoLfxDdA8pcDu^f{7VrthveU}n^Xd-ars6~ zo#FPI=~H(8JK=iLkz-=*fJdlp{9x1b^6V4wgF2im`^)ltWT9hj;W%%IWVRHAp2=A-0+-R;U%}8{vwUa23x=soX6EobmYEy+II%q zp;yk38emq##Wyvl(p(5|L@sya{hTA9nx}zBW$lmpy&}H?4y*J$4%vLGc%=SUO?W_f z0q<&`udKkMTl0lAL*EF$hjrJ-RAod(6BMc(pz+oa1?>hG)9g2_@K!N1@NS$&-3)@P z&MvHygNr$a+~Fgn&01CSudCZKhe4B=13A(^l8ybl<*GQBEmYMgt?3{41*^c0%+>5?aG&%D=g%nAF}(5Y%Bi0FqVsfCq>ch9rIIp70iYf`@DQvKDt@Tf*lF(zSo$t-L zUok@m8U^ec@1PC#Q`IJFF5KRvH>Haz)qiE2g4*tW$+NJ!t6rajEt9sqLtSaKo}Zk> z^z2?b_5z~uOe&?I<)5M2Ue}eo%Bw^27)#R*7JnF z@&T#H4?!k~Ch08ExvY4xqbPmVi=WTf{bnoCIN%?Ps>@R6_^3;dsLrSOVV751!NkD1 zc3EQrty=R%#$3C+aeUtm&4gIRYF=w~u2dmRYqZBPV(t$rM;p|8gRAx}m?wW3ImvDS zD9wJ3S?vRkg7h5Z3yv45nft6`(P z%N$mIh)Q75X8t4ZLzk!Ea-Hs)(KoN$+LgHPvhg!JT7{1EqApMF^0(?Zk4Y9f~OuD6EMUQ=&T=*Zh@>um}n8$IDtp?cX8X@X7{^qcQB4JR0hXVgz!m|T;uY3zLp zAnW-cLS%;R3MsV=;uIzaYA*aG`au*_E~k`QA1*+dKaOW9*;;=baGfVbtjRL1jB^`~ zObYTbf6wGQPi2l*y(o6q<+KIm(GKA)QlyTW`0q>8c-tSj{CySp-|KqhxkT?c^QZ#N z5E5;>aQaJ7HH2veD(t{+8p5#>H1+!89HicTlzkQ-pY(O^boCmn&4s`K*3(kNLTUuy z-dt{sOEhiG@$>G)3!pWuajydxgEW=xhEq8goj3-Y3QQ9Jg;+NbJNgV-Xdta0v&{sT z$?d}a*rBb#=~T_(B;PQ6vd{9@Qs^Wyza>0Cw*un$V`NctsndD?MA`V4tZCawY)r=A z*!$5hF6;vpoUM!MV^r7W-Yq!cX|jLU!o9w{23No(N+@*7_rD&2P`7!Z)(3`2K{~qo zzc+qOMl zmWNLiv)&6q&2d5#g>?rJR|&pWVM;>v6~*B}Njuc9blZQbO6g;*>+$3M7QxXPleTBO zo4P$ss_;v{+}g2IDIO`WIX@+QWva_}F3ZhsG8etc9suR(4N@!ujEOz6#W0YkfW>C0 zs5{u3oun)QO(5=t7j9H`G?{2Wz0vbB6LRca1Y`k;Q#aow`{KJhn)+O75>2xZ3Cf2z zvQ)uosr13@=hx5U{q7G9|lYU_nkB?0A|KlrF!2yWeS&52$C{G0;WB3vExv!(qtG zytvZ!`V}%qH^8o*m*Trhr3!1j)D_gDgPjG0VO~Pk0hsJ7pzXS_m%dlk%gBm?ua%K= zl`uw>7XGU6lZFSAOAVmd1pj;q=HxmGsS#F9p{=!nAdo}m4NJ2+LV^zc@lhU>v|CXM z*N*MBc4Bn*!G^I;qz6{@-uR!=Idg`}Zy~b|J|}fHwG&z&zFr{g4>!2HKkEI80Vd*h zwZ|%F(0qG;*@8Uml zw#=r<0b)+t9+j*={DEx`hhLuf!w~!o;>8C0L;p@d=`Aw%KVQ93ilFx#2;HIs{S;*+ z9bq9@lXwUn1u8L7kUc{z(fODB(QanvVOCPYPZ$n2kKq)YM24Qf#2hMMDosB`JjJLk z;JnK3y2|cV&XTLZZ6J9z_x@0khxV(v!Naa@A1tPxJ~hVzk+!Xz@1|ycNfOo!muE5* z+umg&FAZeY&DKhG>icrD$j??wq(Of$94g-^<8jP7ic--7d!GS1xlN@r}8js%7FAH zMOaTGy`0em(86*bMfW8$l-l(RvweHP`(cG$@UKnn-+Jymx?wi`_D+pjbPrx9Ht2@m z|JfY)P3pd-HfE&UhKVW_+ym_fMki3HG0&?%8RFvwpa&D--w5jl-noDGO-+_a^`V6t z4-ULr(Lq-WQlS}X^}VU4BbnD~-%Vu|vZ{e?^*(h4F0Ok$;9v8u6vQfcZ-cS`YeUzJ zW%0viK?@;f)?|ox@c6AIaDHijHkA!7%{rgB&&n`slpD*H)|3jCm{B>$Kr)C0GF_KK-PI9OBYZ?J@J||0M2H@z$Pu8K7&E&IHq-Zs=~l zS?f73#fz4?nH`oCbbAQHsP|&KwUcXGfUH1xzCFn5vA|VV2^l|t3TdzvW1Q{h~Ev^7I)bz zF-`*;+-kK`wjNUd-9}nK5&{&}fs zi&8CaTtFt8XI(!*1*afQHa+R)wpfGU#tEpMtk2U+v|`|$hj{z;h~H_Md*yvsIrP6U zDg`nKB5AwrP&)y`WR^&&Y{iSG6(O!o=L?vbZt(=vB13(s2*lF z$gp5^m z?Z;~C5^{mxYwOquYl!aI4kLlqMpDi;?>l4nTowm)_en*XIjrbpMDolZNz*7P|NSD1 zXYS*rWAYIpoYRhbO|q!*$q-k!T#@;LZ#m#Y)oA(;o$ZYjO4pvpE=O9`@$0+$4e;sV zj8@{bhO)}d<0N9+>oQGq5HS3*P4_m&|kca_0jv`O9x)T#ljQyx_02`;A!Zza_68DJxFdqA9bzIGLDvFUvj(` zzV8}jSTea-p#qk4HG1DAM+96}3oMqsEKJ8JfgGdCT^xgB48I{F9z(1x9=1**Gi?P8 zx|5yd%{uUce&AG8*ZbX#RwCR?|1Xo+dqe-HB_7e}E4m7nC`+etn%`zL3$mFSqc2R& zgGt_(b{H}tLz(bztLeC!+-Jsm_Y2b1kdje_4e0B)W(8ze%ll53M9NJy5I*Qa^V`Z* zK9HFo8YfiP&)7XDn&_s^gLE;7H%Cw*sCCm@2!xE7u(a7>e(SRPbYuwO@~w?hsMHyX zw~l9eneRq~yuTXuIrWXjOFp9bUL+l%?$*xPMo{kjl zOL&!4r8FYS1~u@KVIs``PcL|sCi9mvCH^U)-=>`q zK&b6Tm>1Dcy#9V_l})KMOnrbZ!4au*ABEfN~7SyR39zU zE#%YEevqEO{D)_5KLxR46k_1^0YJnA4dxX5i8f~D;+vg*>P$U4KX@D84;Ob`*#4+9 zi6hq+p9jQXH07^aGFe5f7wH*tK8zVC0+BwxiPFb8o-QEO4`tWI17jJmU*q2iujh(- z5%<2Z%`1=8$shRGf%FO7>z{m=@ywv5?=n&>Ydx#H7h~XP$833%u)JR&YOAu*o!UD( zqk=>kXu1kW(|D&<=#0@!*O#JPnOv5)k8Ne9K{84unuCJIvls3m=gNR27HFdAO2ReF zuFE$#kvL4ibmQ#Di>B!Adf3be0|Y%iNT2(@HeWgGe_Qbs2m0z`>3RsX-}2Uur+0J zMaP|YbPH)+5F?)OWG(FlVq3;aCvzrIlWl5*5!#1Hp0*AiY1nLsQNJ2k>l(9Y-D2 zBI3ceyOeh01=CVgTnm&3UNLxpnSrS|P!R1W}IelkU!%U$|31lb=K% zm%`l+s&c<)BVe>bX$nQy|1nJqOk!(C z!h-6@ZjSGMsdHY&!WDJ|2ds+QYLjuNp%-nyixD#z1y%gusp_Dy9g?fx))?rkON7KG zOJa=d4n(+kz=7LuI(qehB!-SyDtw=RZFJ6sjnRTk-r)3hQ!U!vJbFwzXA+IdcTR=X z^w%N?&bPk4_@bfU1F?|P$d}H^s<~)oCVyW=ing|)^0tC4RQX##rV)C>Nb!9G)`gzB zVQZEV7YNiA2p+*%c0Mm44x>N}ayc{oLVF04EK?Y`;_i##xhtvaGc=7=og=QaKNTvC zu~?$ueA_EoZanc!)_$LD?LIiscs-}(%*9jIh42qX@?x-tN#($me8m*&eGYC3IM^W> zrpiq-ZHG5%yVd4N)<y+2;+#D~wagRw?Z2yPXTJ1nX$-G34COZY2oH!@2F`a#xkz@ad__`%d% zH6c(!z5xy9qVQ3#I-7z3~GPYD(7S$DitjkMkt%kph_sQt64I zT_Vwl3pPG5yDf|63AfMn{_(%rK*4_z0c0FCn5D&u4@&0{xM>%8L@IUo>fOmKu-%rY zqpE@Qb6Jja!#@PU97ADLF`Cr7c@^Yw`pO^#oRDq6f4>8`0%(lH7kU-2>%lKLS+O+C z5iVhT+H7kFWH!Ir!p|B$^sORM5o|dSIZ+908YD9X#6I}KV~V6bM8hznce{q3{g8c) zHll>*Rv-C1gp(<*Fc#Mbj8CrpT206F#yK-~2WxF0scLJCbgjsA@Nb@iauBNe$43u9 zUn~(eUG-% z2^&MIk zLBU!*OIgx*eck@w$>MFYm2&}>@g#Qz@-Vl<6d5Xp9QpMPhe;nl0;bH>ltKRS0RRNw*2Dk1*CG3SJ$wC)v9w9<( z;cBk_+h&55YU_sCMU!f)S9XFwaAkmv4f33 zFmvZj1Bset@@xD97kqJ#i`0;Px#f%2zhkV=@y~K`e#$q*AE10a5|UE=_qB<#JmEkRwLA-=&P!?C&Ca8IV1` z8MeqrBzHy9M=3_%tzHiEc|QPG7Ff<@?~&e*-Fibya}N>10d-J?;eOKIUR|kO7Qfp62b+`@IC8S zQdA2Okz}lobI-3A~|i?Bc`wqn;n&93NkZA%m*2JJs_bqyPNa|a@Fl_ zD0N>VA_4v2>Cqa{-%PO=4%E#4wo$GIV+Dz(osBTutEEJqu&Z`j-6Vyot!B!K<(F~W zsv?o+jG(PRF~&}|Fn^Ig8Ppu&-vPQ^6G0a;lRWPNBHEB$vWGXJFmB&K=6|2I$i zNcWR0mg-5ATywhB$q^%YvEMiPK&-{3h=?y%ga?j~D*WBBSJh`?MQdEf1wj0$_WFh9 zH4xh$41p!~jQv=*jp42|n@kSDIcwECeV>_~7?l0=BzkL-Uf3tp8S20MhcezC%y}km`cX|(b6ev1>j`! zPF1mKZIDtFUu+qV14v%nyoCMOh|ISfRv_(T`33b*Xx@x9K2DP(;kooP@6F#L<6I!=6I&-W`TEe$``*|bN$DS-fE zy{#fBSuHX^g079D$W1A`jEtD&H->&CQ!N3N<(5EBURV2|Om2$nX@Kd%SbQ z;$fg`Ce!(n5J(~M<%q4An@e3jvD^c48qzPsY|G@fl{J8_{%(72gXn0#k^Z zrzCzX%2LBGxgk8Uc)1l4Vaaynf}Da!vD&|5ooUYf9u8C&+dtwtM&nNKi+WJ^3PN0y z94N7+VPuwd3<#1^jRby_Sh)(mWJ!Of?$6^NT_XVJgtr@b@I~kv&p04|iDkUI^FZt` zyL&LIQuiV*P--sOy(>oCP!8E0vW6=IA=IQc-`=VH04csnvK6-7F`%93DTQV0c&-(> z8}&TpTWe%809M$po;=c!o9k-}-{Grd0~GSyuOt%2p^CBFQy%s+^;3nGYZQ&*KM6t- z+`hL0VkY`-jId$IRkKVhP<1<3U;T$d61}%}N~bX6j`p zsiU^)arG$vPPyTLiSafue4NBMF?&Jk)?91phsbM!Ue#hv0&9F7cqcAkmzn+=9xxoW zHZ%Lmq|-%xo_r{w-m))?Ajk%JsK>r2T?g@Eu(e~U+hil1)%gEo>doVty1MUSsJXQr|NTdo886qM?nSz1_Lf%r)98$E^VcL<;tIYIdeKaY9-}9{4+M*|3VL(W*cP^iYL&( zPO>%9J2wl+Aye8a_TSJYgI|gjFtI$B%m3MLxjq4a58`Ll#whDhDpkN47mT=AQsu{Y zNm-Hy{bi%|KVw23T@EuIcH?;Vof>u>CH?4m<{)TYKL>VwncNop&?JP#lnbU@ZY)@N z;Wk$va(Bt%)_}7iUXy6lHl8pL9&G|>OG-Kbig-s}+sh%FZj3qJO&GUqbPW4c_#b;d zLE;FSpzWv8ZZ-=g5VO{O)#~oU@gtfqHHNebxG!^D5XdmneHI=6+=;dpV=1*FSUjs4 zddwK2wnj=Kq#eX}z_%5LuAGEkzn|?KNMfMY`|M1nVqNHG1bUWDCSZvRZOE)b-!U1f z3#PdA%btk-k$`0u0E64v|K2C&&R(t+Q=XEIrIdcN;5l2)V^a7qGJRetd%=TA48Sn>!ioZxNCag<62J=(Ud`Z9f zM&B*ZpB*Fn48g;ZoUJ{C+znA8n$amk$8+V#MkxM*Oq_vG%zg50H!{nmiW{U1rlG*=Npf!27ub`pc1`FN z2Py(llPOdffD?gfOPBq|%A=PUs+T^no(D^`Kt(ZV1~8_fdGKT$QvvgRAEdA^+Q*_)&~25Xg!fVRo(P#zfXTSK znJV~Oq&r{72yV~A@b*$7B2dxXr_E~PrxS6sNosFM(dnot^-MgJfr>6Xm5pt=SMByDf33S#)lu(SQ_?@MCIX8qlK9WRjEdR7;c_&mg=mkln zzmS*$qMmr$Z5VOXngb7sp2hV$`QEPQ?}V^{f_u^3Z(?Ff((+Pny-IC)D{F4W56FNf zmm&+~Cum~3v?kIq=oW^JsK3>s3zVlTn`)g#;#TNe0fi}&uW-4sWAf+v{vb;<9V8w- zcGGZJ#(`279-bgtRYsjGzk$}L1i>-r)%s_Z>1{QtjiW)n-|%u)1t^Vv*POJ$yD! zc-8Ks`l@yiWlG7sxfJ~p4?AM3xA$tD35h(GY1=-H-CntR22SgI2-tVQM;NbqiV@W4 zyO)Ivl2aK0kQiM8*}*)ofB`IgsQiYoaV^{1QJdy{cQ;N-6~PNJAjCslsF-U8B+F`D zrv^oAiR#iM7=HERWkdz|Jk-jdCeLgowOGpYNo*N&N}(WW)IiVbpDAzkigU*%=jc$k zxnQweGekVNS!i*D6Go7Zqb$Zpc)_r9yHeG@3hb8aFlVpHVfw0dP7F!2A%iDO4;POG0+@||c{Aa6G?=XT9>O4M z0=bgS<1XsfWo&_Beg?CyfCLb(;B{|rX=ephrL)fM>gm*Df1)fZy$3F7Ndp!E=~oIu zkU@mT{fE6vlGkfc#Gk$A&iNBzcjukeky^zq)!^K~to4_iD?{)tpa5(eScZnIWm1Vk zEJYx9vCcA6xN-pX*r|%IG|u3(3b}>+I3J9n+YWgt;}RQWZs5+*Rw!Mcy^yV{%rJ+q zwz_g8)5?Guq0&th+RK61YC3o)Iu4GDb-K>kRQi^*2Kd^|KGsyM09oZteuj+!rZlTs zi1MIl{Ta=NZ+rXew1DDUYc(B)_(3Y~S`KT~?*Ml(O(LY4QGuGORQfLZCK0id(qK6W ztC<-iz}XoR!yPyvR<^bR{Q18)ze$u^i=bws)4@&Q9K%N7Cn5c$Md$4q{d08r&~Rvc z-!x&#$1FkAs`+nM4wa=$GQo34t<#*e9|YIF^KN*=@L=F-+*=rnrq*b4;Hp<5a^S0; zviB;T1Y`&aVrHQ547VX$hZifX7mEH@v;_rSZ81W2gx5o_(9e<_=5e%{)9 zC+(d2(i(T(_T_+B>S|Go>vO^*0|m`ZVMcr723YY@O*r@&Asm zp!Y*Bhx#thy;=$WK^mbQO_#%w?u-B0B!6NvKiWWD!K@x_nRhW!TOT!e5NFPndM_qs zr>J%0)Xh-_khP0)7k{Ay+%B8+23m;iZoOE#oT%w#%AI5WPn^EK9h`FZ)#xMa;xo4W zP$XJ^)w&rjf_(d&6))$+^FPI^9x0AMJlk8i>$-1R$TjmBFjVLU$vJD*Ya0+$Dq1U- zQ>8E^mvEu?U3iS9GgfGJYx54MCNX;U_xJ&-nI5Z;qd14A9CX_Ca@x534vssv7M$XRm|PCl&0Fww0En$D2du?!&gvYaJBJ1z1X2DYaYfF zH$MdhNP26n7*(*8JUp>EWn|7^6z)SQs)!f-(E|z7S1pjM%!m`h1WJOqtI9kKOWwpt%jRkWP$A_PxsG$#GWeYFM#euX2F@sJ>eIh)(N|e znkhK8l3t`tBIL23ZTr#G{_GFhJycqB>X`q2*~MK-99;a^UAhJ`$+}86f%zxJ%+;TG zLTz;<Fee99#V8{FiHa3$f*6fsL3&h>zSkNsplY;w#lXjSr{0(d z2EN|s`1QY{R>x`(B&02G^T(6wv6;gpH4_;6t%IVq9I-Wdj#&%@elKgn^-t}ge7HCi z=!TE?qB3?06o8}FTSmP|i1@I%!*Jv--uLpPcl{|Ye$_fPR~0*7JBSHui@9&Lx8!~6 zE|m)WAc!r#x(upnsdb3%H6lO^CgN?Yip2jgVCo&p>b7P@i~w7R#Xx|=C*uK1MrGGC z7z19uI13?8IJ)8WIdfh zFj8)>OAK9jniM>2oxT!Wp|i>dzeL(VeiN#XWUtrk{^&^m(J{!qbjq4y&2;7_1JB(| zPX>oNCTRn^)3bpCthD_~E4nO$tZ>71c*ToWCu#cLcK0V$3y6kX5Lt*NZ!d3+BAlW4 z$ZkP%wVrEmM^nd!#QD2|yMI&hmWw#Wq!KrvSWX`VR*{{YjTiI4MHsS`u%5!d$M^zb za))kIqM9d`f6%IV5JTmEOM0lbSE$cjNz}Hk`3X2F;RRK*BM00kA%Eawz=`B2Tzs?F zIGu2$0!~4FAn?$2L_Wa20BsYk`B}{?zfj%}aj?v33P*P| zC*2X&G5dJX6#5_RkcMm7m4Kj%#O6Ons-OUGRsb@B$D5NpKCx9CkAM`>6a~CZ)#Fb)Kr&80xvF97Sk&GqL5%A2o>hUKF z-_By-&LENj4uGXlVD{O`VWR1#*}Mko#rmDKFS;#|xHsmrjrT z(D4zflqmYO0k9t`9&WX+T-=xApcV$d-=1STVmmDejQeEn$x<-ra22Bi%v&O<`53$2lMaC))y|45|K@Jix_y3|Pg6sM=bt>eu zp0Z~Vn2F>EGZ}gY)Vn?nzy1d0Uw0RhDe%&O2kCA%?4t?wu-QMe%_wUazkuvdPZ7eJ zc_VD3sW;cFDlQ(6(vaHkSZ71+oHsjEcgH+drdP-WKyOVZcWdLybRtPdQBR)P0I zFLhev3>aKalq%2MEf%`oGgY~F?=;Mj8zlyHRZ<^}NCozD^TRq-jRzeKA%6#lq4(vf z#L-~NON7r6>lr2R2ku3+@qnC3aszr==;M7t$H!u;=geI6R?eW3bO|&w_w!<2`X;Zq zW}OkwU~p-4jsdlh3=cm)%0zEe#se!?^T>i#U$u@gAHyQkINLo=#EzAHijLO6Rkb<) zW-O>DWDhgeqaj8Lg=R>bCIBnK|AQxq(yT6&AQM23+h_usTcAO*j(rBVs-a4G_}_99 z*9OXnwVpL@K$D+xg0rQHvNdMm%(&f=Vy(p)SgxQnIEPg9}+j;+z-GITcUc2 zJ^z*y*$Q!7>B91+cMq+!6beNMuT$X8{ohRGts5aprc>bncl`H2VaE%Jl~ith0d@7& zJvgBVnK?oxu;?C_&VAMT1$ImG^n4-RCK+47kT2t2ntC~L6hHoDGFm)U}>D{V9<$RJ@N|sk+#J;qI=Xr z$Kz5R@`BI30pYOfp{CJPu+=MHEJDY%rxPbjM99>IiL#o^#_kdN^@E(L*VkOMj?gU? zu=vegrsPker*M1j0?x7W%XFAzIOi)wQ*XxY# zX~(=mR5?v@&CoLj3_a_)E`$`d^E$wO8X(r=XMo>;@l$ZArbZ%BAI|)(f!+5Bg2017 z#o>}OCwqxd;xlM?n`7AY-dhkNha9wME^lItEQe4dMICuPd;2-yDhB8L@zHWH!Z*4G zt~iBr|8cWZO|G1u0f43AekC=Y00ec{2O)D?81*s|RBfRsnE_mLx{dsbN>0I80*mgVZpwRV7Qn5)GE@ihB5|IhEKA3ksMzpiY{!U-+O#TTu^@ z0dOj4N{|-&d|PqXUG)tH{haUkp`%E~**7L*u{VH3phoME3lF~8-*~5@J3agV1o2?= zbM4zk2N%eY!e}23*9vtsPM4f(ZR44+!9{PR<)qav9J~ufYtKrS z#_L7s8qB$x6X>L2KVkdg@0z7aG&nm0 zrKMppqnN6fNIQtb7*on4*9XOWV75Z70`74*Ed+lt`%$&)p=SJ8r{a%u}Nd{Ky5mDym|6DR={4mz{?whlPL1`h^x2kKA0@_nZYfrV$C9H3*K&(j$41zS3lv3fEY^bMfk@ zT_pIbdj3adEeo)T2XeE-GSZ|MT#QsaXiPJI5HE^ z+-ZjGy`CpAT-dZGDMV&1V=huIvIOGB)2~`9(c8|WgBq7lh|^aL>Zd#FakvsPo~uf_ zsQ0I+{F)I?Jcq6DRqtc=#y~%7=kFT7L$1EYq6427K-8GtXi2uTwlWLqYm@!>V$!4n z31+A+j5xy=8t}|il_o2Kz5G)&9#^x33J!!D`mHRPMQfE3Q6MBl!Q*7v^6_)2(CebU zbGx@)5h@ykZYICFtg-@i@@b?H0S4cz)rEqDA_m> zF&vqROMxBsi_;0JZd7L>7GGs3fU$#%+y4V0(|4>wy-S8em6_P2TqQI-j809;nSx0o z=z)!=NpG|fZp0*0v^tfr&OR7gO;Cj9>Utks%>b@7VZ_n89SZ1w0j6|=ECkfozLzcCXICZQuktb&|C)q}i)mj2-eZjnM*R1X(UGhcYTw#olr^mvW{&Ri7}eB8*w4*|WZ zi|#bhwF~F8y&{#4-SnH%pX+2Zmnq{5phKDjKWzI6KW0{n zNT!O8Ryg&?F%4-#dysq8o)tCfjB07qj$!uXAnSXdT^4f{ucTImAGs#~VlRU=y$=Ji z5vGa2&|G$QKP$k>q?1esYJT4JOoLP9fO?|`$pSAu|7a3(94fw#pJl=_B~L#0)6)hn zjE3wJ=}8vvKApUR4t6Nl=)a4+ja(rv-8MV&0_K{2)hZ^fK(mdl-3P@aS@6fr)6Jg3 z{uE*@!3|?RzGZ*OO zYy@JT%wc0Fq)P~YQBd--K=9SyY&u19ZVZLP8U+GF>p?%edR9yDA+KcRA$bg?uZ3$v z*W^=%`)B=A%%=ipntbhN^q~Ru zFNojh>nt!lv7P5cVB!S}8kA#T1<>Ft-&$=l24%niOx;WvG_NSaB4FC~hJ*F|uu%&f zA?r{o(jB!62KIm?jdLT$6$x82l~DU#;qJhKZydqv{0;gN1Wz0S_ENq+Jb%AL=mKpn|oswB)LAFcJL!7|Gu-`b_x!cBxihwX`uCcMe*hvJ*dY))9{D znb`o7Ok2PaC|nMHs$=zN`0ION^|pR+HYtLR?EHV8E}&{apbs`fU6a0Fo5$xojtL&? zq%q-0PAGZxQly!gza>SHpEp_A^&A;os$*QOGVO!_YjTUE`R&cmjX9nsdwkiOfpF*m zi8su;SI5LSM={%A#2YLoSK<0c3V;WOC-hKF_tWZ%Znz4`4_*0IeWmM8NeGCKcBsdY z;>vdNQ}mM#O&_j7O9YM6=ZI;a(s3iiDbN2AD?>#hb3VMS-^`&{;?dWiCG5tG+>Xg%C6Uxv)* zQ1-B>-qAi7dG8}lz59|zMvHbY>2 zLxg)W?yr?7)$#7ozvV}#JyZ)zteco$w!$TW-2*ePG3P*;F|T z6&bT@q_FBEtg_@@{pO0|vCjUp?~JYR!U5RN&`*^j6>Lr|{d-{>W z5pwmH4fCRXK=p8+#MWHH(>NP>h44rv4+h4zl%3j!_*Y9xv3i+6P$m3`IiH6Lu~t2) zs)P<5S5skds*j2R#*e+8UB+3J+#RF8dytO@vn?-v$7mG{ez0b${hP|3F%#KpuAmb( zv~^zssR1=s;rE|dBa4pOuJ|Q6@~q-jv)GA+SIp>}LaXJUZMOHXjHg$< zdH;jkjRA?5WK4+(3+})J7kly6(AUEm;?&0hc8g4n7Jvu_l8CJ!G0{-lRQt}MI_F6D zhP4M=dejYEigB9eM;7T=oh{b@g1Pb^vZ4m-`6w_!1aI6#Xebg4pGQcx5>D4!xjJ6C z3Jez!vU?d5yekOT{t-8(5)k;V>h`Z%VKA>(dbP4y{?Ov^9kicHI$WS2^Cusm!ynfu(0CZI!1*`L+(j|%?jR^c zTP@}Pd_KkGSa;qCR)9vF#dnE?7t7_H2txXVM!%3|g^$P&569R6ig7Wv+Yq-Y3`4u89yKRY%Kp zNBCW8xMt=mi@kq=j2t06kSa8E+l`gJcnQj(iLi`cOG?Cr{L*QO@uFgrUZ^$R(IIG{ z?8#jEv@Ch=NERr25*YuBS{RJsXA0!AGdH>$fJh4FCAYwMfOAi!%Y$HWOgCXQ;Ta~T z<9uMTg^F;=G%$vnw@lnA&>Chd+0^V z1Y4MJq2-*J4qYO2zJe~jld78kIMzjkF;`c79Qy>l$=OfxDHU)s_cNmkcsacIj5|

6KFrs?#cr$AZX_=3(fgnFnwqM%Ofu^YD$9v=~>GKl_=ubwn-=C~~ zCS&2fdGB%$CJsuVNDuSq+8DaGWLSybx*K7F9z?;%WkU@rrIMwYUOYS6t>K=#x5XIK zcd@^6*&CkE;Z&K%_>LBFYG8ruzA#=(8 z4b;hJ;a`w@IDSWQQVN+UvMajcXPHLpCX66U3Sv#JAyJ$Mdu< z1?6H+5cRruZiDiEOf(=yoCLe9_|5^v9{LvX3&`IvXKy#HJ`S8NxXV?A$3EVyBn%$& z59sdN)CogA%AkdoP&FzsZh)TL^KaL@&hKA2<1v-6ot8tm?}1B}J{+_5l1E${5ZUFI zZpuT)Fak%knFiXi*(rCnHM+;*2jsf8IvGYo2u;osxR@XzCP0bRQvp!4M5erT;=efh ze-q%1AMo3S=&ej&_$2@wBmAeix=|VwiCGftDW;pKzDIAZ6fy_T*lJi|@SpQ_c-2N+ z;Q6zot0oFfP+?)FQqBw(a5OKr`F0(pkpQuc3c`Qqa8Q6m?WOV>j5zT3YOgXkP1iTz zIp5@M>?;8rnowXN_Br+xmQW&{H1ybkCL;i>m#R+UoBEGm;;oE3x zHaIb8Ccx-`@7^DSc=Ln4WSWG{<+h~_Kq1?dwKR3mO@O9k91t`M|eRJ874&qja%xpgvsGpDF9smAbaUP(^iENqS`j*b(or=CUNL zo+(M5`hZ_g3Xsn8_eF1tKFdxE3!7qs$@0mqkGj}k{FRuCDxO+6Ur&%{;8l@IrEBJJ zN(wgP$_QEkv)(raF%mq_nXAc6dEa86BQItSzwXuHi1m?~$3Fc%Y`L9aHdT-|w`j-s z#BDZa@%HyIATp(azlwY|KA@7)g{DkROUve$m(Y#Ow#En|CL{Fk_dwosr3R~r?}0(E z&Z%hkr$EbaT*S(7IPhG#tI3*aPwU(Q86jKE%uS7Cq)hTAM#v+ka%jBDtNsyz;T&o? zH_n>E!ylhIdUph=m^_w_S7um;YoWDHJZjb99GiZF`9H+%G*kqSD4KwuW0$4^rlQG< zqV>9O-bR9RKNjtb9pBOSSYKx*E*_3x=i*nx#UC`TTeBojTP-2xm(`E20n>NQtqD2otBDm#q#lYPq^iAzz7tC5 zfzQAt3Fd$!Jk0yK6=mjar>8WygW!lklzXfL=?+i)%TTWhYm!~_6W%blqyqDH5iMU` zuVWe4Y0HyiwlwJXKHYvhF)ajW9%?3@P)+M`z8WuukB;9tu_Bha@9_I^ls<9>o?HRr zC^?XXRcQV<@bKZ)r(G8a6#oo@wVbL=CBtBZQLD}!Uu-3s9i4tvYVlgu=upQ<1+5ep;|L+P&$6vg{rgO)e62s?@@@LrfY?&Su$tUK zhUOs#Vh9>j6vxwP4MGkL_ZiLF{Ah4l(5zv!k4>8jQ5#d+<*Ix&X|FQ0gaiM~(CA1S zd)AM^SiGhsPf2$MA%FXx3Y23msSac> zt@1dp12LPM=U~rv-;qAT!lghpDSeS|cQqBB@=Nu)Z}wkMxQCb!@nB;V?EM!bda-m_ zY&)tmZ5h?pKvf@qFkCGmAQh%%;>{u4PU^U@Q*m^mnIQ!_-he8yoxFW$xm>zWUMNq9 zE>H>3Y2E4((0~}GZ~o_pKhB|eKs}A;f7UxyI~2*X*{%66W%DeuhG1#hL`&Q)tJ7h` z;G}B922jERck9FAlU^U!zTL^vyef~n(uBxIYP9_;SAt_9pJW^{q0qG!Njb*JZmk2RrN8LCF^p2p9u|3%856%*2k z%zkyksgpy*S|sNh0>Kw__K{fiPWZMg->z!2G|0^87I!O-aQdTPoc^a(R`f@0@}{p^ zJwVG$clJ9jg0upLW-FX?W4#l)D6FD=v}%KTVp+WdGxvJ~JEmNfyIS7I6aN;raw6#= zD7uWd)=sMNqPNDE+S|)aSlJ~)pO2Yqa;lYFWx>X2ZP&jotqq?fTb`+$RL~_ac1iao zmY8Zn)&C*VOTJmL@i!w2;ZS=5B183O-yWsQM_X3(Ov2l`HSg!@>22PiedP9j5HI4t zwv116eR97sT$qJlwFc)~1Bv+k?5R^CRUMVqcjT+qQq>#^^6d$BWEjRPu>(26x0;x-ZN-zXAD&CRG z&szA+6N!2ydM##WJy^lu5g%iBGvlD#gdH>>!jG`#sY&bCsarBMkxuX?n(|FdxSIV9V9V8> zW_^DXGU7KAmZ~ACT<4F9^p63OCyR~HAX~eprG@b--Q~zqX?qI&J8_^&EK+OZ`VVjW zfzS(6G*}1tnrJh)>j0=HIc&vVs!`o-2ue^PJqt6DCc`WFvO(iPB1pX3DwBvLMrsxB z3+Vh*YNR&zzva}Jjhs1yawDyvqYW$U2_3>eSQIZvPUHso?C+@@9ND@u-SzO?%82&Q zP-5Pys(ovl|Sl`OGDvtar+zbV+|{JO7Nhjdg?3Ih@p z3G!$Xo6$czU#6KI{8#7?tJkj7g)1&jTL1yc%C}{YNdEIdQqv~zWl~5-X7#eFzO z1V15pl~sN<*0Z7IGZ>SQ>qP~=bHW%;tSqgfVv&I2VnK8FzUJ zSn=#jF*Z#6RLIa%ad_td;}poyb#498vOqa(35_qv{NS49clbBxPy0!iM3^(Xeyz+y zzSYj-aM%9LqdN!+hnrKG)M4sIWQgY@kX4~ha*T5Kc9dne?=22vfQ1*3Yh7(`+@&v} z7jp=~?U(bOjI0^Sz8T&O!0YM8NLRX;ud6Bj!YV?4GYUI;W)&|jM-Xd-l5}hk5I}B z!H8SR^0Q_swm&aGVnbN+2I6quNIlV2aZDV%AkcW7WQfkKEsFRK(6)8DvRhSs3iyd_ z(!8Ei%8xK#T%u8|PT?tX#h;Xv+U-?z*pPO6%vaal69vD1~_cID? zE@E_*Q0#7--_6;-?~$)|*qOSWC6u-!`-L2)?FM4HuB2+FK{wW2vEk{0z3a}KqoKi& zm-arX*px@P890FL+0cqqKs>#=(C=^e;C`JJ2r$*;1J-|>V&M8D`a+rpzI@mLq^|KF zrBOvPnA$?70YM>dLnPYP`HC#Ya2Zk4-Oi*+KX;2EwRvnpDWk)ZhcUcgzJ~t9;_GZTv(+2le=Wm83F|WlF0jDr=V85EJViq4=~JNvR?l}v z6$i8$lfKU_o;f_#r@G}n85||DnZooS1o#N-C4MBr_}hDaB9V1T3jb85U7hIESbrSa zxZaZ5P3i;qjVv-QcB)MD47HcoJ?bj;_`8YcOGhzf$C-dHK@~Y+%Z}fhz+!wh<| z$I5RE{rQ^#l`Wag=+VvlN2I#; zh+ zvyI~##C73}T5VYFKp(BJ_RzI~isP`^Er3L5U3m?jDce(S-44^y(~p!5?L*jSoe9&< zY85GtUger%`sVV=FB)DVNQrge)<9_e0qaIL{;^M!GD6yu?*>EZqUqyZp~DH4cJK$Q zi$;6CB}zS#H!w7ciWiD7dcW&`GbEg+Ep_=;Cc7c0SSrUxPJdGK>gB2Sr-qWA;FfmD z_gsB`79O7Z7~6fn@N4V%cgjy1VicnouZ@ZY1n!HtbBW&9NodYjtu6Dv)K5dO3+u~n zNUB))XzM@>4A)bI-$)wiZ!=uJv7AF!!a%!e0+gNlAP-+nm%}La2mex_-M;;e_RKCt z;#_N2_z{*a73JWo$>pbFwO7dR-n5`kj&7t0QXJCL0Abw$B7_ZXcI2ELC> z^&Y(%A&XT*rXTjMEt_KETbHg)B5tK~aP&T>1NYMCyDehc{Fmp)gzkFYtl^yeC(e5=q>u8!G#*(UJhVxUQ<-Q&6`ardCcExc++-Br`eOg^(p3_xJ2=^<*07X zI{;2LY6KpOFSN@TAX)SH%`XhbTKcyZuP+RP1*M8vLYLEONeT8(V_75F?_@D2JEPKO zn}%miz4cJ-Di^9MtV$1^>zX`8Hb>21w^4~0b8)5))eMjW0}7R!HGNKJT2DJet?0kd zM!V#fqBfXM;X$A8jUE|TRcp8K?de@1-kzxZ_yDPwt6xdMs%Zxr@53BmqB6Zt6-?%+ z62z@m#d0^k#**Y3H6Zd7gb*BwSx;%r$oqpw6!Q%--bSjyCcvAzVYu=$-;0;rr>(I3 zpTmwHqM-t%P*Wb{Vxb=|O?D+kvZXBjB1vBYdrrKwDREP@cgIj_oD1dJ=)0$(rhn&b zJo@s~wdOeixfOp)u@ZBg!qdj&r`5rsNG~iG&aDO2(#8)(J1eaWYfBlrHs?vnan==D zf}~w^TjdI~YHZZ`Px`a-G+H3fhZD8qx4&igW^c{K{kkK6;Gx$JSTWn2s3j3D;(j^q zY%R`bWvKp(?S(mWW?~t9v>v*Cp5u^;<#whlh;KL-?%kH+K*Zx&&>*+AyUEJUWBi<7 zk>fm6o~e$y&yAkEa$ISN3*wMIT!6Mhs+rZ*$i5+Sm&+YVmrqu;ZsSQPuf<>vqiub! zz+WPsTVb@3HRoL(Z)^)NYerjNGQiZSl{IqbbhL%>si}rBUi;ZI?ytj4rXYU(p>*JX z%&TObQa>^sOXusGcILaaZR_%^K5P@$Ycf9|I-Cq|gBMUX4vy$uTcI0W$-`%b^oC%1 z)asiL-#?#fd)6r@7uS~?^_EHoD5%e1!h)N_BGrMULnO zeOF*}LI90uSo7+XTVDN&Zp(momV>o03Lajtdxnd;_m$WOtqbX8=C< zR(_G8KUv1*xp{smHL9kr@b`%0&KTvqUf!4;%-Z!f2D81u!I@lV|MqE}{OSrOk$3iPoC!Y{b1^RT_FE>&CiZz;_b=`7?w+vhD(YG`u#zb-xFR@;#rcsXZ+^ zQ(5KQI-}8Xoext1lDF>*)5Gqbj7s0I@yQYL*ps*Ee=<{bK6!X`?XMf=43-9~U<`PB zQIDfv0a);5z|jP|G1%&x93ozI%L^x9(YUK{ZmlB3*E8mhjJoZPf} z?-iyW;rg@0U>PU4f67w8_018CvjYr1`RJ$V(YdNW6<)_*tgw3jTYBlCoOO5Z9RJb& z?zX&9u1kr1Pa9a}pw@BA0dq()x~4+vLt+Q-x;2ia{+87+DyVDNbu`g2Sh>L@dGl|T zCEM}CQsq3q?2dT>eJM5SeP6YPBbA3+%U6STvq;;__P!R8} z_I39WMRb1ETGT8a;pCH+k`zQ(tnij$VKVS}&M%#l;)Ee4IH&RIYMQybB&0e^VAd^yutLs7OxT5Y)9z>P60e2R|FR5WK&& z=ib{mv@>|?ew7?qqn^WmO?|fN_DE^U>xR=%(sRaY0{!CEqGjm4CxQUwTwDI$oLJ+Q zS1*ePEtuCTH@6Vqom9R2s&)B7zxT&gQy4*qAZYEaGzE20Yp`qOt(%!$Ykw@u;s+&L zH7_v1W1O{2CWoTdCQ* zw(d1;ZXf+p4I)n(tiBPb|ASt?&zkL|Q>w@*Pk1#&y%DkV8=+QsW*r8(8I z^^w@hV2j>^Nc(;roVkK*u%3<{&RQc zk?$+F!k{*`T*6*_V5s}}`SkBvbR1Qo{FxL}%FFz&x$Bl3phW!S<7+H>EVlJdfZJV{ zImh)o*98~v9vx5E!H;Hld(eWI;nx>)hf6+30jLub^1q$f5L-MpQg5;;HE0jvg>oI* ze7?ehT+Il2NZAm)>&BMIpo5Oa^KQ!&H93BZn_Ev(y71XOpW>~1H#YxPHK6yt0~he6 z<;hT6IyXx@!1Gjxtcjhm`C;NnQ*~))bH@dkT^P$#BqH64R+Fk1kYnQEKn@?ggyMwXm^Q$iiIMl``b@R&kgR?>!wU567a*72t z$e6M9A-UcJk6Y{{Kenl)zK8RVf7?gZGs}CV1NR)ybc46yvB@h&fWZexCp1o|OuE&*67zBK%Z;2k2_?1W6UltWX1&RbCijO`>xq z#!&U?<*%2@4cvX|rptCIv?rt|QgG$$AnKIVnM*41AZg>Cmr0UR<6h&Wa4Yef3$Di& zSV1U#@KwuP&>p?OE988EPKQ*Yy-z}!F&1A!l1qKNx`>xylyl0m}d zoBsA}>C-qgKm2?Ypi+*`(DO+C6TAsF{P*PZk8@Gs2BI0}Mxqv&!idVDomo#@FtPj? z6DuK#J5@?auCC5H&tNcfT|OT#9Mz97%UGigw(KalBX5Xd^XxT6EDg4SWz<+yl)dVH z;*^j0+YI{?6*H-+&^Y)WvxzVP1_{)T{omt+XrpPuPeuubAME5B5J>TzD+z^KwpIOQ zkV(6Wj12QqTBNxI79{^)wI<-}o^dm-VN8(|TSn2$bJ#D%!JwG=)>k>WOLhNtX!U!B=!YMnbMr2@ma{DQ z&8u2?{qO?-(og)I?}57D&RGiLsXCd+XY)9i1|EZdg};zWXHi#lOqm^MVaIPv3^a6_ zfCRxc`1JNg<0)5z)fdtMq}Ap9Je5mmyEtWBtzSc0r+oiAQTLj7``>^1)~x(gaC4Wv z*Y?BVk{D0>P>v_5^Rv!(Ct_VDd@jGRm`k_Ec`V3rG$h51*mo=CQ%5f=7I=wjk^EIHOMzL9p|rbiKNS+Rk;-KjXaQ%R>$r`LAiLR&9a zQ>+)Bbl0ibil5qd09RwMv+hl7 zfw91g8mLNdp3KjlbTG^Ql>1WQvR{H5M}KJfs#Q3CXu!4jfKz%by}{VRI5050 zcdIv6ol#F{@rzRxw1rG9wcyXsO;#MS4o1b*jX zYFAb_l_RE;8?pbDM_o%`%;{Bq3UMvsrtQsCggZ#ikH5W=pM3Jm`YhkOA0)2Pv84t1 zqZZh5j$6W~z1fkrzd!%QXY%}_w@BAA0c+}Lsq4t8-cDiWshlZ%_0mJf=FTKW<-BCk z8h&E+uwN_|>mkctlsay3&OOtHH46jYSWH@r=$DTfU8-Mu@hWcK;<{gBmLJ{Xe~DHf zApZ)y4Q z@jm;Bh?+RUkxL^WYcx+=y;ZR5(Ci`mUNi2kU9a!X-NGWbTOOU@lD|ay;uqiPeX{TQ z@kKq$-a7G;Wu?i9d*gyiH#^C7!3vsr#hpyPk1lk6H)Ol%jz4l-8lJN+5I@=7z)CiK zHrTc`;dZlS$r?f@w|8c3AS>R|kZ6IjXe#?+gGhawcv=tVX`ThM)37hLE#3+Hz}eQd zJzlY?A^Xq~4_-!%|hs|LB43ux&E4%h> zZr$?2KR2vl!y&&-%nQqnk3K97_w#srl2?qfEb4WvtKZb~$#Ut3=OC*_fWu<&l7oo8 zv!?wfpcd<}GWN`gbA6-8c=4jRWo$SoUo|KHMFr=%j(py;n{%o!&)C@!Ep^bvENo!IEmT6`i-29o|e`bVBZ1lK$au!fBRS0rr8Z ze==F;X=LXcw0(fF&~ind-%Cz(5CU0#&Awf%gK@_9F7?IN)R%esWlzY8gJo$*$#-E9 zWJZjSe!P5Vmifwvo09=Pg~5Lpx9)e?AyYaWlbmZut~6EW{CWPycB_(HqJ?+eSl|xP zuMQJtH3T7l!hpg5l*H^@-l${Hr&72QdUO&<*3H{H=hVg?&i53wRogE~K|1=Jnx$Hi zuk=_gxVMbgy*rBx-wqs646Zs~w?p2aHzv&H{qnx2s=dT3wSUY1qw2k*n#$hqVI4;i z8!93SLdJ?p$*3So%Qy}&f*2bhAVg)9CPbtK2)PajC@rIijI^i-h?Gc;(xOtLAO?sK ziV%`W69Nf@aMP~8!_0iw=Y7}m4+SL2z2~0u?EUO#?>1qw-MW3x-%gk0+T&6q{mQL` zpVe-a8ZU>DU_&$Xot*V>6fe@j^xU6kn{1yGJ}2+`#{atJ+2K3C{q}H!ZSmw2=g=|y z9TW6MY@xf(*_Oz}>mE>kRFT|MX+9yql?UoZW{DcS9AbD1PNNI8rOl9FAD02@TPO&L z1p*6ocVui~?#q`?hE~m9k|&@Twxdp|&<@GTWzB&mCvD0`1_yp$0G343l2x33=a+4P zU!yVsXD`wR`w|t%5hX9w9iqUQWmr-GAj+pj8?61~dN8S4TR^EE;DW( z?K|avyXn#d_Y&;SIPt6V>=Usk$<0RUQeAqh{g&OW!~37Vdwxb5<7F8;SV~dJmXHNlv3{++Ia6SifmUFH-PJA% z0SZWnLH1h)6(Bf`51Au#yp-)9gNX0Wm9%LEE>w}eB}5u6^vaCXKi;T2z@2t~aLKzf z*EQtD=k)zm-_OkAK^$C3VQ?0OZwNN zDSYIYh%s@oPu&UOH)hK@cv+Lj5;*t!64fYnoC3{*%6&{TtTMUTn0K<7WQ(X_!yp%M zio3&hP5mKFgDigBsqY{cf~D(BYgWxS8q$iM)tv{(gbrP{&L`unTZ6*$4_WQ^smZ5$daB&6+x^@XAiPZ`)NN;MeQ}|Hi!K%r5!9GY&mmD9>FRGSGJXafL`P2`a0@l=@}pe9FV~l2LEM z=zWJDMwm)+qAfy{U`ZWdt|#E%S#8nP5mLZs*o_iCAW)I@Gs|T`?zW1Z5hdAR(^6Gj z_I!GAygzTOw@xLDJINFno?0~6=%vqUu`xUVTkqY0n&yyr6r8Ic_s}h-ajx*vMBa-N zE&IRqOm33cb~^Dseycy3n~`+%d~W+p;~!m6*L1joo!%4ZtEG`r1oGcGiru=q!1i8k z;%85X9{~a)SrZ2e>ZQ`2!+AV1ijX=?c?;ZaF7%Hk@-XYYtt?>5)2O1x9;mOzJ?>!* z`c{=-wKZeOwr!BbuqSxRpEA@&zF>IhRij6?*r;jsXI&{)l-5Ksq|I_C5BDaIm4uSa ztALKVJymL9R`~11FLfV`NT*1j7OI)HxfVN{&i)#3WC0-p`~Rq-t(&)NE~Btm6D*Ky zNu5uwl-3#zMbS&tq`x#CjdG$al=z#QR+V)1DDLFzx@`cp^u8Ub8<&AuBiX7q)Asfy zbZ%lzET=8N(o8g?k+vk!{nA0;`?rL&VI4w z**J4k)CaD?S(f^fzG*&P{)X-kIQ35_!UlpQxOK$Me2)5zGC7{=y3eiug-55gNt@=h zk4inoLCv!qUMA9-{l*b+`fBjXhDE0UtC%es>_)k%^o(itwAK()CNrmZfasLHk^rl0u_iB=~Z~C5av)*ghU&8(hQ8Bi-zO7)xxP0@)&`;P6 zX>&JUN9q#hY$o#1MW0OVL)wFrwCOQJo62`EPL%a1+@24ry#yfxUMw+C3rx6aLgh2f z6m2OOjrKnbCPM$Nsrz~(N<}JjmKN|;STRj>t+EE@+9c}zuCtw&M;kT){)VGxkA4H9 z?ukDrDI^so%5y8b^+x#T*JqXU`rmTP9XHK)il-yuW1-7y&*CbwgLvMZh5~4Wb)pMX z`3txy>jIV5k%u$o8x#i<&=oMf+0<$e3u!SReF#cE5~lkFAfs;Fp*KSZ0{1vK{oG#A z^lIQ(zO=ykp=dsv<|HSxU(GaP)L>e??~U^W(`5+NZev11W9Mk?DO3Bt7kBl%qD+p0 zNR2(5xAuJjd%7A7;q-SuyF-lZM_RUbg4@+h8I?e!=2d5Q>lux#L>^u~wnR3z3`Z?>=IeOoj#5yo`iJ8STsXbGf%4hIy zPVg-(o|qjR%jzol^%L4Dwcu<&6vFW6@p?~LPuf5{0f+DZzT};=aI(oPcqe9}I<%zF zhYla@NZdD(LWw0xi@%&$L*F6LF&on<6YJKO;l|@tMW>v3x=t@f{w}t3>$l6^{~&L- z=8KD!gJQAZHDW4aMC)!RIA_;oCe13`SDp>98VdGpiA%rurR&tEo{Ocqc0+>FkMHw` z4&590N94zA)vwXH+%d^Z;fgzO#jqbPn`f z0b#*pnd1d|4=F^M90%+*$jTb%<3?Buyc2zr&1aoO73F`Dh5DT_0(%AK>BfKUbFXb( z4D4Zwb@r;FL#^x;K(zNs$&ku`vI0aIUS8xh6p~Hbj->m!(Y|^Ae+`jUUQ_kOl>Br2BEb6$GvobWJSI7 zIUvyhI=$vyckdpUV2Mg&jH2od=a735#SZ_eA-s<|#R3!gxARDJLCrEt^E~s0sZl-5 zq#9sI&7x0OK?S%Hy*&?S03mWKXxEoY1UETraBdWoeUv6>zj#e9TVYVUsBHL8J`i&c zoaHji#|orC38B7vD&~G;VS$~b6$SiV?u^o-4F7SzhTe!AGj0fuVJSw7oi8&DH#uLG z{?)d?Rba$s_5V(16&f|rpQ;EMW75=S`Gom84i>B%_a%3Z2MNGrtKD>CJA^ZOD3&Y8 zX!X2$jQNV(uKqQl6L1uUxy>o*XX!!U$daRZwos`)mg3n8{f~-z~R$?caP+8@Hq{oxr(~+85Gm z0+K~PM6Hv{0k4egHP^1CgF>!ro}&d_%GP92K;?l-fTowi&V15Q8+>CO`~KO)5xHS$ za`1pmjCoD>GWq>7@_>Zue*TmnCfe2WaAvYsw(JL(@&6W?X7Ckq6>>t(3Df5s17X84uyzw@oDY;rz z<6;8qtjHW2Iu7=*XWy6PEdq#CxtAE|Z^PHo)>5-2C>WGqOwNEw77!IB3-!C6M5hs* zE%uf=_OI&qDW!B-1)-W>#m1Dg@-u+-)CjD>tzetz7eOzX2ss3a^$G)qqTfMPrS`6J zPn_(N%P8<{yLb`7fhgIuZw6~!p}OGu!K!4Lk`tSJQ^7->m-v$As}`yT*r%GIGLC~h zaf@LC(+@P{fm1D|kJ4HO?o65T=}$?srMw8gRQX0Jz0T^4+ayh1m4=x2f71Qm7pzuQ=z zKX9W@NsgbIn46#ph$gAW4T_Koo9rW9l(lWD?OUu#$ie)~PU6LKJLP?gZ`y5JONgKo zF)|4fA149CB$YRA4iv^o7?MwPOhuK4DyRznbJP*p$oE@$1id4NB-sq9@?LHsx%9DLggU9>ZNB#3)sJ8Fwcq zZ|*02X;arqT+JFx$gk?$Rh;Wr>z#g22-o#ouy1u7GxV5CRSZ3}+-b|gc+qwGVTb{) zx0f)TERMuZwfp_vO;RP|dPqBAKl!;g8BB83jG!dwqEMxYyqVwFh^QwAA?kSWn!F^i zy?Y_*{qxDD4qe4l#&Vtg42NnK!pIvMUx=_(oNVAIW1nXK2=#)h*3*HYu#RP1IpD$3 zh?A^OoNzd%A8wsaw|Wtyj_#0`?ofujMV z^!;WqkJgi05ini;0=(6Lm!jxvMKOxapML9Z)7nP+8o70M*sb*XeWrSTudO5zoUf&k zK;gB?znz{)(qgKZu^W>QFHAKsT5R9!3jC9er!UL9;StON#w+8{HJTQqw*)iM2o|@j z2(@nf)WMJSd7Ry!@l?ZS5@6CmJ6@4;BL#K(tN;G;WS?h)S@qdRJO(^Ea6Ng6`{*q3 z_)pP8qkIA6Um@31&=*G?$t{!=obZO)l%`OHk8Z(5Q`TE7lY98;9)a~lSOx^Ha@a5h zoK`9(=rtSyh`LP%q~y{`N^izcy@>kG3)#R@#;w#2|8f1#)}+b%!ltGKlqN3i1XzK>iPbnvN2tIyymFN%!xwripNb)x(Fga>>7g z*>wi=!Qn6yS$A0(T{nP&e{Wcc^ko><3<5ILog9x+G0{2P6x=!ue|{$6Z=LCIu7g)y zNPCuAt#}dU5b+khvkkb?;=92soK&7s0^Ee3>6J@9R0)*}QydZPEy-AfK@FhR%mBig zs}0v*&MS`xOnx=8nGl0)K;5MaQwWZ$=*h%$+?{;X@`LMbQ&#=uH|g?Z_R53=FUG)P zISO$q8a=e_smt-v2OHa==hTvrj(5-ZMFED%{T*;~;;Zx()PM(%J_o`B z>xaE9sCRQ#)v1GRbP$wirs))SwF^53gv~_L4VUi+HqADhNQ$X>4c(=cR_XCCUfTp8 z%^p!bR#bnbyLf)!!##wpYu+`#fscmmO0_s|b(gW{)ix2D2KszuG2gWCe^%WJcF>8B zhj@9&n_VL>58MyzX>jkj#`vN_p5F`en3WuN8xry!j*4=(4oswenBp zYp~~G;XfKDp0KAZ%-`Wn-q!RN(Ze2U*1sm{5&B$e-5w3PJS%3l2W1Tz2cGb0E$K6~ zsGYZv%^~X!!9iYyN^H%stFlO<+cgjHOZ`qgxT4i zzV(G>RSp@lttOW%)L0yNH%+mrfTyR}pR_N?VXk;4FQg&tvwww1Dg7|t!Y;uH9na7 zDib%{bse;*qd6ayK8^NAGrLGE08)Kjz{yQfoSYHUr*L3AG`QsmRkto~!B(glKb#Zk zmza#@6(j(Pqz{OU;w&^Ddmmx4$ac!0aZnBD9g5p2Ip<@PF!=C2vQ7Ey^q9;baArRZ zqn}+lN^5MtT2g}xh)?n>x0aBb7IX_fI|XAtgJu{Q8P+N78t(@Mh<-1j>UIGDQlQmt z=n)(1_(Y^(+oQbz5-$k&Q+>FQoNzWH0kTcQd8!ZO*p%<_MbPYV-W3%(W#? z>g2dSRh9}K_X`Gn*~x@*+v-r4Q9r%_QP7aMLMNa%pkl(+7 zzjM*>UEfHA@peq;ylX<(Oq8WLNkxKLqSUCEJ}P`j+WE%={VPt|IgYswN6qzHyUHO< zn#3Zq$vJ=MtMsKZBZw`ypaU(PI>H|)@w^Kf34yQFCsNi0GEQab68O4*Q^$cy@USP^ z|NiGZHzU8y{&Ru%-9sWIIHB6;#MLbywSx6e7sfGuy?HA3LMvu%PlE5hs2-`d>}cl> zkuEv>g^#O$Q=eQXySqDoMYsz2Vwi4Y9_g9Fl6C!Zmw+L0tfCe4=_|68`7TCQ#O)76 zMw=V_+1oBjvJwsN2^tS(*z?0K0aRKM-)*9n67WZhV+iC4tLG2^<;Q#uVGa# z!E{;}`lX$jw}=Q{f?Q-PM3;rHk_PVRWp@V^u6E;Oea#I``;4D4eQ~15?<-l>ID9eJ zh>KD?c%5-y?I?BnY@^MFv!Mz8ZP?Ha7fWg_jelP_VxT|`zY2W%*3Z8e`7sbkB_=LD z%oQDe^TUt5{AD^vker+^VrpOc^@lkq?mXT7EK)sdxu)nR#x=S?!>4t|A>;(^Dk-Ci z`t^nuxdo>e6X9RO#|&e(g-&$U)t*&8_Alluk-{-P6~|guZH5w2pS=6pTZezEy1U(9 zbFh5!i*khpDz@!GRPnxot2NFZIrg&GZvaxS+MK!xH#rTd|Cs^}dt_rM8aM}Kk-viT z#h1NVDU#IPG@rGkr$yPgyd{hzIKKAH%cInHx;Z$KgMhys^1Sf8tgf_S>#v#aX6Dp; zlUiy5uQ6BVGY-!6i_Zc^B=rdc9IB4Nf8`I>wCZ=AVL{NVA4Jx$c+Q*I9(gk2&fIE@L^)wsDthb!!C0IZV!SQo?E z@@Af)F*=9&i{I|vxb@-@red&lq|eNzN+QPZ$8q3&!g+ZLF!lanOD-XHdF9P=^mt}YkP(2o5C2t@sZ zd?lW*T)U#O0Szvc^}bp_@|De)g9%5!EXhCS{I}Xu4lH_H>hhK0vXY?bhzn5~b@cZM z);}esaQLU+AJ}6FIub_7fK>lwuGA<2kF?Ywka-H=tmC?+`-f?Fvge z6Z`J&;%m^$UUH`I0S{8F^j1}+@#1^=$9mtd>qrAYBA_$NAZ^J5E z1kcCb-tCJZJ@5VRYuEmbF!nwYml6X~PFzZ3-bd2USan!pM65-zLB9&1>>3<4Poe+y z-us_Yzt}-O5x6)Whe-?8JxQq>>R&XW`o#Y}f{OrN|NeNKQvzyV~0>_?KPDN6HRy|;DVVfS!7yPph1lSHu{NO z3zW&nfRS)@`M-nXaeTR0>0%6p(3v46ge5NZ71E${@tF=KRV*y5duQ%|4(46ECGP#k z0rF1NI1$2*NmuR8{FDfcGi==HMJkypney*T=2)mPT36z0dL$T!Q~-PTy+;#}-{n{e z7(2FqT{i)Sp`n6+?yCFPb|gYGRG{po)?fnnJVEk^Q(gkE$%U0xU$zCYN}~1&b%l+W z`D!~6s}*I!cMHOfI&YIHSs+WnqZ^3g?iX@L{i9sTgQBP*j~5IHw&QR`qjD#_;%R53 z-Cax?23F}dyKc(4N-LAE1*=KkNgrR0K%exlXdfM|QNDQ%)H1wJ;QZ~DI!nWD6`)F6 zjr2*5`QIdu)`m66A>0!w0D#LueHfiX1aw8T?>K2~nW!Kh)X{#5>!Lm_ z=IsX630)&y-R}5Udi7mE>a%z$;E-QAlK_6^2O+Y;VjYINK@^rm^x^w2Yf2n;*{KW4 z;oH5C9tJ$m!>=IWIdoD@epv{Y(~kw!yTyS>V){dmP4G{_)Y}iOP)laZ2z(Duk$%$} ze$od~z{SAIbzoXRSg_)B$!*E}WtE0h;*0*g&}UVyGkxwy#>4+s3?;p+D@*2FEu#BH zEY2l^&biPL1@B#m4sk1k33F4Eq)lx=HcI!&#OV`HqF}a?<@$-A#P_ z_LZTLP@u7&Uz%}$4VIFil4F3L*^EP;J1IhSi| zlncrRcZ4#l4W3pm%xAa_(iTFcs580|HRt1lr^~lyxLESg`~X&p@qEsFd!pYWS(=txmnUGmpB1 zfLp&iW7hP?uKMg$UkCqjC1#l{-sn>lEnfKU*=u_BSj^cz`R)&XP>6Eqn|aA$=G4te zb>_42j2=pe7xKu*M_B=$wI(X^i`fia(b59!(MO?;4aNlhVR9p+O}q-nM+QD69)QER zbofM_tuoE$DejvYDGgu%oz=&M_~^=O+t1-d&UdQn)w$$o#lflPIS1Kw#vfZB-;>3u z?7xr|ym#+hkljA(uz(Mtph^^d>8X3t?jonv}%QoLTx7(ZMH)%!dRydE;+Pzog zZb&Lj!l&7tnsIke<9HQ3EuZZb&ZIzPUV=dRx=GTO&sOG&A4CgNqUW?bzY;Wg*9LK? zx~kOFz3oiu2--Yyyuz9{v)T{Yep=H@vBSei*Jb7DqW$;ejX(+wxR2;}L|c(&5>Z8N zfb^QdqP&IVdF9SV+;V`(1&|9ycpsfI-?&YMOJ(Q;_EOfPw*1~o)DTTJ+al`d<%Gfa zB6c#-9aL=14F^Q&(PEwjMW08~m3{ibn7@lkxJjEHoTyIz=9TL8Drv=`z7yw@+{#DK zo)8QCHRc6Rl$PyllMa3f7eY<%0P8g)h`=aLf=ngr5~xEB;ir{-ee2~{j2*l9eTf%X zIjpHriZFQ+BUwrvO>$ay#INN{x5wlQ``7Z#o2fmPSuCf8>(eEiD2!UiT$E?a`aY!B zRVAhXko z6;H+PM!Y%$hx7+qGX~PEr9OCU<9lNc?KmI3>|3?zkTxq=sd7PBFBmD1U?k*$cm-Cx zHVnCjl_diP?+1E&BeDurkcor$ItxUqz?9fokS@3O)1>&8cUG-mK@(-VvsJVZbH57< z-@@CW6E~I<&YgAe*Ot2P0{IOa&xOp_*MdL>30l^EmtW|eV<{}%v@wsS$%Cm_deE^N!pXp5*Kz@WX zpNz+l)!;}TJx=U!UH3MD{YH1M?y@D(Dvw$og_!kWtE0)0Md6`f2y|PzhJ}@3M?l+E z7PKeo`x0ZTlVR&|ZUbTEFJzc$;u)@d5|GNBetfmnt1(q_KipUqNxvz0r}D2C6QT6B z*)kb=Ehu>W_J>uv;n+N)g1-4WF@%rn*Sdg)c(`YZP`H^J*;a&=-T=FVx=V-g+Tg$z zsKnb_;;}75KY4%40_YG~e_!$|@}UbVtK`U|!Ggi-4y8${OFM$XOJ=SORw^_Hl_5&g zq{5b@gU?HjUfwcO^)!t#kJ!FN2_2bm7WCGL?iW5B-9Lt{f}Mp8py7Ua^{{TO47yAn z!L`Is=?PKq=vA766T);!l<3oIz61JQ!{N`!o&gW?Dmc-eRrTZNO4MyDQ_-im{zHCd zY6%v`caWVzr}zMux}O?1#-6_acV41wq4CAbyvEWZ=lc!W&@hBUkCFDDjWfy1M^S$E6NO?QB9n@vhM)L~?1jiKb6pTA8t(i8&j>X6HYX zN$)$IC*7Dkru7pi=-gf4M)ere#rE`#kdIHsf@E{z_a$e+v^k*7+yR4yLxy$qXHT{r zO<#Yli+L?4fgMD@a#n^m#TsS^K{D%++U84-7V+id0p4m0)DRH?iI z2x9)xuYf{m3Q%z<@c}632z7VQ6;MTqVLU##-3~|w;|i{V(=X@YR^bI5%(N;PMWFVR zQv9JN2X0O}{x}Rt?yw{j>lCYnfG4_M0ry;kCj#igv4lywXinO_mzY(GK~eEYJ@mq! zM+v}pQ)VgFm_pVW#EpcODH0Ds#lXA)iv8n@a;8nH)i_0$N6uaQ+nxQ)=3joK_+RIk zSESGMX4%eiFhDE*^?yd;zhOY)7MS}*q%u#1eirzwvkK6W_g8q>lb`S>T>e2lE8wUn z<8)(Z)NG>&G>?w43=t5rx9`y+c|)HCg-e_8hkrYc!I~NYI3b;dY)Qd3a9ocCjbLz% z1E3zImptmo8vb`mxCD=qY2Tq77x2P>VGtEN0H-|JGwED6-= zYeGo!Fc}|}cg#OXLKP2~*PhDBp^VK^XID!e2qoe7M!%pLeal_N+53#bJI>l|_TvS@ z#;)L;c|z&aW^BVWS)l^GV!s@-iQCyb*j3>2_Cxbb+h(evHthVQ5him}()f5djli(0 zo&%3Bv*~aQvIhbPh?=eNlN%{)gpXf|Hpf3v(x@*0{OP&24v&7umye7N*oRoE^{dD+d~93+Zz0x&$h~!< zwNzU*?q+~#9cKsGIHLbz&Sa0FF_Fo4I(+5JvE__xAeeS#;MoJEW}aPiCyZCUX^ZYY zp>6yar78WsgfHtd}|bocHqY1hINB-+qH#9yVfN z?yv{YrVtwBqwHb+R-*A4kBwLsUN-nbbQ? zb6nX0HAC(pig>JWf@7t}$EBF*q*DS++}zHLACav)0%YAue41kJE75422UR#~-9NkV zI`@>5c;IPOHNYd6Ec72-sd#j?FFBGsTXWz{@~Z0F;pc-%^Ch9B-Sh|29-y zc->0JrPEy_3*fXU2G`;Qe@(%&{mnjYBHK>gs6wJ^eWn!=D1j6?IlZN*GjMjK8X!|m zr4tB2LhZacr1CE6%=z=VPX~7wjFa5{hG~x)HMMRu**~20@nO;KQSud+x-Zwz=H}ff zJ9>bWk_rsf^m{QrYVh8Wg+^3-KNN4J0-qglU{4n#&EfngH)M}%cD(WyfU>jH7hIzi zA954Yt~>6qk-`o81`Fs}BYCRI-go(l5FMLCh|wW?I0{Op98q%T!89}8Rnmpe@Ad~~8~oL)me ze$a=J3HD)2I(FQ*dSqUy2A_@?Y7Yiu4;zyfbt6uOduFdPq>Ho#=dr)U%3g>8Q8T;% z$}J?g`~(FgV@cT(U6hhJhWdb-Q*8@D?M7^3X+vP={Hv(Dx7s6d_ULXACjG0%uPzt@ zkSxd`yK%5fBgZtNERz4FK05Nl(-z22%A71_fU1QeWO#78YS#T=3u{|kc9kCMZb-}D8p#}y9(tre?Ureo?10Efn{zyzhoC!i+y-S8XUYH2 zOCC=?Uii<4vR*DTuw(V&ho^M{3i!_^rkysiP0xL)G@dK>|}v<4fF4e&*SFWXSEvB zBOowyjy&RIFaoA`zz?A8R*fvE&nq$ZTI!^Qfr1Iei+}Vy$KJaSfv9YS@7II8Um`#o zbZYOBKZ=zBenWr~z196Mvv#a?W7`;@B)mpWn963FATyQf?8HM3qu?UXF;9Jt2gcc` zv11>PKVPapAotR#g`&xGtnry!9ZVoHjaQoW!Rl-z#uX5FbbP@U>L=ob?@R7$9+@f> z%yuB4Y2JQLc3k)aG$+Aj^vZYs9*N#q zpsxv-&Q*i;sPVy(=-Y^jkep2VIf{JJ3a!j1?Iw602gMg>vu{#RDL@o!YK9$QOYYE( z1`XkT#d8gdFg~<}FtWjUVa?d4a$?c953|@x_l(Ir3jee8q{ZjJ%^A~E0)IK=CPmpO z{9rZtK?R|Mc?Y#T`nM{|eKZ0h3HJ_JA8DHY}5!A;K80WBH_(Y0J#kiR8R? zs*jZ_JisT^VYryJFuD=Z>QRta(UPpTYyqU!P1~mz3A8t%^p)qD52tS+mf~8f_z&!o zt@jA@-RE%U(@lDM+_U@bdcxd~MEFg5bX&VrbsEXM$rIC`KFMu6>o8AzPP@4gpm&IN zm)IS3q88~xT@p6L0(Skwsn&Ohw>$|*>4gTM`kDX|H>#@gmc@yvVp1ceeA5CO1_gIqAnXYa z{%Aw(0z^GSH+I6qFhaGfebU9WvyWS-Ol6`Hr%DesDd!>mby;kz_!$Ydi?Q?P_Sd^-`B@ zB54mY@+fi;M6t8$1W{^tc^_LaeE|>eO1))7VBk<9wp@m+TrS_>Lps_sj~XcAh2DD% z-&b2pXgdx2$d7pIZZAVvEHNiR7u9L(5XL}l4Jp|)4B-{}x<3gqaSf`lW0*dH=`K=B zsi53GB1)fk3k&m!tvB@(Neo*;XlmTL_7l1hNCoj?04il{l9cJ>>?f% zEzdD#>Q2ObUlIyr4FLHlKreU`^nPNPt_3v*4qL#pbnwheMwMArP@fveZ|syGsV*yo zqX)P%iN=6OUgpR4SN9DYQWf>Yf-j-gn6&#`itaxW(r2-Zk6&TWlV82F8o&7jZ?9?l zh?FUS!(7j{TE=8i>`h4z2gHYXt>=f!`)smy49>J|s>agBkl&GUXgGtTqc|?NTQq9` znx;)2?i{$*Lm02vPSL7bINBY;*``?AL+38oYe(?%ehd=3S3yf06`zxtINPKt^7}y8n-_20AibBJv%YQYp z%2XaOaQD`$SFnJWe5pV2<)M7P8mn09_%7LBD{&W>j_c2-03`N^a-Y?9o`h|3E^PBE zcZ)#J&0?x%;9VZiOlIKS@l)6}EH~(>0@ZD>sJQsHT$Ib328-YohNLg{6a)=H)QtXq zHG~FQ+X{))YWstN%xiXrMJ~^>)9R{eo)-_|&J7eQ0Qz~`MqeSev>j$l@#Xf)=q^qZ zkXh74Jx7?bhSKXyNj_~Dn^2>7hh#uN{pODU33_)_NNw^^710#iAFY9Sa+vIMaXkm% z1ED{&Y49AN{i*>rCf`oi%417ZTCjZ!O$#xN(k*lm<{fz}VKpN|w}STLeGqW@u1omp zC$3FB@7A%_H09XE zIP0r5o%8PcZxC&*`cjb7Rpj0W&Ie`8`3YqeP!O3(Pi<)-3@(0`zUezhKPXC^_z&z(Ry7#$9-__31t3z$yw+cV)lK9WHX0MWGy6nh$$vV+l& z8XTDt)VbLd#TqitRKA=G50F09q5nanQXZ+WxIE&NiMskmTi(bs9q!ID`&S%*S9A<%Eqcf!XH zXZ^I3l$aZ8E0zO6>}KTc={CFs6$@2E{Zu- zxZ@6S5NvCuhV*zCWM#SfxK>f#a(t(#@Aw4j*`wf9n3l zlD?A7=>rZ%Q9bT=Wz!qX#h}ZSK5>PSYSD>%>uK3e>o0B=4004P!ENj4V=Sq2Dxvff z$5t4SEzyqjR2K3ae&(@|%W!815U0Ep3>0X{*qzhVa*=0jns{PF3H^>7ml4rEVi(bI zd|2FcW!GfsfK*}?%HDzU2BC_;B(%P0zl%lTPs?5^BfYAi7hR#FKJ&O3%!u~N*DpWk z_M_z2lY`|i!`%r?wk5z?6E6LCXRl&FE1wLs^d;`as~5uN*fkTKaAh`C;18U zkM;2ldIFB^6dEkW&Z$0`GlQ_$kuI{{hlgJxJ|8LKe8Ob>dUhc^3IfRmIpflX-(|rv zP;ZtX8m=NJDr;W(GYG=9=VSIgiCR&5#=~upSTEY2R&B@)~pfa5{8+;4=vYKIB9C z+6m8tGny~XmYDV033*PrWrf>2>e6*Fv$8An`h9%jFMNY-xp%0^CC5Hkw^aG2ZQomBa_&J| zJ+@7#t4r|YrrfC5rmJn(*iEiS6>$qshk>`=_Pho77tdPl6*&v?MEDDHip5Ob}P<(=nW^7M$(wxz%1D@4y&TrmJ58Pyl%WP0S_8WplA{dohU6Iv0!W_jXA z66~?ROVRX?3QbE9TGM5>z}Hx2cE7s8S*pRU1H0iOV=>R<>0jAkK_C0at}d)Q)?yc_ z!qMhHU+u0EeER&X)isy7X3ISsWt|d3ojo*KPC*D!1qkT3qY5JCAPomA@F<{qOb>Iw zO(kQh7PniVsTY@Ty;N2>T;8AXVnzr0G-#G%V~)v-A$@xRw=qT$tP_)hbD@YHt9Ie+>cT>F8`{7`%Wzw1Luvcd>E9mjK3a^7tew8n<~E zzHCUBoR*u=fonXnWlVQojuK`fs}AK6^pCxguT7Vvlm&Sk6bp>*qD00>ZZBe@!1JJ` z*FM87TfR^RKC}j26z3Ut_q#D>I8T)R>4ez7Si)GQc}ZMMp2Mqmv^`m60i9<%y=%gA zF?^iBO{C3V5p7J_^o?>&Sl@#-5@#OEJ~TlNL>!2Q;ij2J2iU^Jiu;X3OqGz|tkY@+ zynmd_Db1~CizizT+)Q(6JxBcD7hXLDeTZl6Y}%%FXXwDS-Y+%Ts|eobvplz1Y(JI8 z*VnhnNIF+l=~17c;rj3N2bd{JJsviG=0PKUU{=iF15t_Kl?G$k0Y0;(-{4ze(2sm$`V@{XD;bV?Wte?bEtT6 zPzgh7%5}6zB6~C;zKLEMSLJ=&RJusziZ(G1t*7F#F#dhVzl87qWxgJJW3sCWxao+2 zfxJ_g{=PK?4n zGK7rb<$+gfBB&p>G~=*4_Cjoup0v9V!*+`Tgh{HW4YrOxR&g%%?kNKBTj9NE^nq|4 z!ytR);{RV1NbBI{Om*7l?Kz=7i3DOfDI2h=HAa2RxOy8xOiwEnDrbB>^P|!F0_J6*xr-S@7$9-&3 zvU@3XRt#{1`6kZ&kh#DCGlz2{we!&5HMeW?_%iUnW=@zYYP)8|z_mr&1$~0Fvh3g4 zm*;hW3-bAEgW92#Y{)Lh+ctCYXc&9>EG4Q^^@dB76|O+E3?Ze7)!y`440<+U=EsJy zkTls3Qwr}nGwYPHAqknkHwbo z_I35`WnkqYK2&bO6Sxx;KqCat#v#xljgBAm)5^~YF%06;rba}p7OM^QHMRUx-CujG z)L~86?hoUuiGX7yuheOI6}aFhPwv?>>kmN~nI%<0g7ViILpf;+by0W5+IPmC>h!bb z;3ttS(u?N@4KDgO6p5-Cm^HfC%Ru;%P7wi|{TCBhCpEEe*vs#N(H)41pHmbKlPcm1 zeD|BWJ7Xt>y|QAHBg%7>lIqCG5#@C+DVt?~!)h=PEDh)nz+4180!AnkzhNeccOT17 zWe0g7>13Qd9h4h(j88qdKUuMWW{ggsz~7sr3%FjM;qv{83v*MX_Q1vLgfX%!Jcg(y zpsOGlxMZi9H1)7GQNp~xugsKbaqbxFtcehD+gGFPzBU~*NI(3d1oE*_TD7~EOw3|V zv`wDi9;`Vr;Z?wuQXqFYsM)y;H_$r~q1T7>y@2xip(Yw~sY~Oy9IFK=EWFX8B$Y(YX6rlceW!@3I9#9rH ztYRDMZ1Hp7^itT@VY(z@)2F2x^F!OtLygrL5#|GJLW^lb_#iWz?Y~uC1eTn*vD6;O zCq%UR%xv@X$^w!_R6@_p6M*u#t6>!YaRPn;@Gwyx^pg4|@fAM5>N5bkmSVbipv~T% zDZM*cgO-4_VExe}<*PND!}E;p8k$4)$hL)pSgFR=`4nLClyOTRDK zXRa(8%}h+BZXw_hOU%^YunO4E?3AmcjZ=V0O|ZMiKoq0t8oD1pv4HBuC^oFa2>{|% zo5HIo>UClCpdc->~<4j&tyo_(4*++)I)U&a$Gfb1Pqu_GPnN z`*2iif6VT(&e%j*&fS@T_ecaZJffxxA8wN@hnEV!wMcs5Tn43*9Y?gk`pg|LTy|jm z9kZ%I6IK#$LepnOs1%|aHzzAQOvj9L2%xvxhLznG;7MJsO%pv>@zn?QBk4yXRz$f| z>c13#-`kVFVSX^iW)`&ZFv*iV;s&Nfvx-DcCGq30%Uz0G`&~%!@M@LRBNHYdx~|fIReVq#bwGEc_4|^uo`Ktt>_^jjc_`5jtm!wlAr)S z%WUB#wQFX+hql?Y-zgguYx%yESyiO(Go9xQ44=_+6CMW}4Get?{k2lrk@9w*uM5~R zNuM&z>F0W63u0fTG$00n55JMcO+{44!moAU)X=DB(1lB`V{)G zvoe#k(E|Z1w-S1X?(1oDq6w23&19Vkk5#z!gDyHJy(4^fx&vv@9$U%yK>eRbRRX>Q z-IG&(aQ|aAw9{NLiP(N6I^PC@h^l#(gUsh?xBpkjR#yusRs!lwpZ{vTAc*1BTMd1V zidSk%B88AFz?>}L+_h(Z-XQoAW{a$S37h)(tsO0tdlH@3qhR@AXt~w4iP~QLnr2L&zu?U4)q&}Y6HU6 zIq|bh-7p>oNH|^;w=gT@u@6s%TbhT0vPBlcPxw$P#eSl$JD#WLz(~YNU;C2W+b#3YBwamr!ta@>f)gdE42!7#%ZbGYZ;-%IW1{r&y5 zkG4nLbIWgyc!)I+~I0p_LcQZZ&+$?pLbh{jY*OR z-J&~)QEL{!u*UR9ZmcM{cadk?&9^cyE}ff}V83FIU06njS~gUCF<03s-1KI5P4YGO z@#MruZJpKv3z1MeVw!<#p1dfxuLg*egROtTU}HKh#oj_E#!Uqi*OBX&0}al!uxD!V z9L@|w8rC1DO4}5+ZAV1i_1PiXMx&mUr=MI1W|nzeBg}LIEB_?3ilQdSs>tqouxDU* zzanYXjfU1Yyf+O;#qlXl-jlR$vd$Z1?Yr4)W;~QFLU0D7gk5B7fo4{pl+2q1b|SEk zHG|0Doo|CXHLRN_4)w7#Vj+)ZQ`OwW)HcHC%nT2;`ICl!mR1GM{>zouYYu&~uk6X( zZ57&}N5!PDQ;v^`r z`a_4VK7T$vuYzO(qBnf3NlF++W?Ra;%}a^%P!ds#T{ZIVO_GrdKcI#1mnFcl;D_Vt zSl=W|1l<0lhx%8I2^xe8<4$6DRvDc13lPT^foo3Guq-R$SehFGcLY4yHmp;s_t5BfbSQfDw4zfgtgwu_Zw(Pi1z`gqvVPOJjx@4FXXuV zO3E5IUhu|;wWnNeRoyy@E&{M^JWS(83)&m>nC4(u z^tky3y}TGXJPAGd`|X_Y+hpzjsxt|;MO(XH&y~Fvi<oa3pVGDb;yERfOnm!-zaM?rA+&+VnSC0NSvl)({D~FLfrbTAHK)T>yJu zGbmmgyyrH=H1uS!du{I%KShA(c!B*)s`=qih1Z9qQ7p<>=o4^uz(U}Z+UB2p3!1=x zg?@F$DlWfk+ZI}iv zAV7hYB)dh*YZ26#wxbw|MAjK?0Ay8R<@%j}UezKd^Goef>jd-m7Ynl|&OXfeF&~0c z6tt-**j4_@-F`$tTaZK2b-5psm~^nM%n!cMyR6bOA)TFREW?0Y%!RvFjw=Uy%TEx(ii|p za=J{}jQatl{Jn|a-o^XK$e=6=ZW^cTtpzcJh=CX{DucfGqT28%bC|GXfQ*%;Rol7h ztH--NVU92%^TQ#~8O6$hOvQFHJv?K5wOTa;V)3( zaHHKz)snCQJRcL*Fg?~3rx68ORnRKb=zQ2;karkdxkU|eA!yuIQmh27+00y0N=*+m zw1iI>VXrD$SvYRfVfu(_oZE8KR;I&CvkAe@89qqcHInRC$3<;vsp3iMFb!2+CATBp z6dwZk{4NFjG8MBhaB^Hi4z?FT8xSoCeP9ECYS_hy(}GC7cp`Ow`$0en@Kw+^XakTq zz*s~y65ZZBUAWl~;1OJ&u};`&w%oUp@~dDTIZijyfGoRcL~^$UiKz7f)+ROp4Kv}G#+capX>P*AW7^9PVlHt@@PqoYyTCfKj4k< z=h2TJRTv5Vldpnb1zy;i6IMKNt&?1A5$i<2*q6aNSiq((_ExG*_kAdSKZshC!)zq# z+rW#6&6C4e7{pAJ@Wpik|ARc;Kl7un%6*49#PQm^qmiv@@e03r>|sjhUI4{c7YSdj zI84Ggmi@b^PLB-ksHIdIPJqXE_|$=wvm($HhkQnHK-i`VvjB42cA+z>=gez(#-+U2 zVpRLzMK|gR^Mv6IA{U~?=0N4Xc4NH*tFmyiaHvU*SBnSNoeFXnFoIy5XM@KlLg77J zq1$73+G0uzZB+ogd_r^kmbiSQ=Z6PrAFBmFPbBM?W1m!7?2#Pmqy4lF)d>J(fm0Kr z7De_hq^;Dco$JNk=+C>rI5tSsf38L+G)a4Ryxh(7<;t9-l*Gv}K){ys(N=W5rjjIH zzLGQ`;~WzP(qqVW!lb_R^wyC#CA2sZKlF$IjT90-g>3ZdFa_;MN_PSX8!>CZ^>wcT zzifiLA?1)DRY!KI;OmPBolx}-Aja#%oU2oOGaH~;SIvV_>T$GhknoJ$IeK8ZqtkhM z@!iBszjiu4HL_XIxM8tbX3LX-bb11f-g9XUp@y|pP)r>ls!3p1PMJzSMoDthQAmp} z^Lra3@vWe2ZW3MfO|B_6rC~_;cZd?=oT|^5y{9OJy&#DX+*vzF8-}ZN`XR` zLPT5?Y(&KqRfgUksb*#Nfrj;6*od2;axYhuf&*@k!b`4!I;$Qic_L;e6V!r|*^1@h z9DW30ZRhspN={^$Z**Z=PZNkw8&VkJnA*<0vSnb)9|sf~JO~XpjAS8zEcO+) z1*t-UGX`C{l}ghnN1%OP6&7m%B$eb|+y6+a&=I-+{8;v-*vVmHRQ-Y^rjqzbVE}zn zgS}y3NL!BUOQIFrhpP>!@FEI!d|r*VwH;kQ{Q#%QT8)?r4aDap>_PtZM`@(_D$A;v zRldQS*qhW-rm?RqjKPk$1iI}zW-CJ?O8tez6Oz%a3%z$l86=%Op722kOT zBSyo8iS&MZm&*ROOg}A2eC2{Zvt}Jsk!{)*sbwe_eNcAUB+l%}pt70vz;l)XVTBiW zP1((#kKfy-*m)@tvy`W2G&~IKbxcM#6|?I2xiXKe{IRxVeHIj0=t?Vfjz1D44(?}n zptYdjlE9w8UIR}+vjuX?!$`z-`YIS2FBORyHHLcwUT2o})Q@sdb<3&i@wT>~^_=lW zIn22k+-CTqgz{NPM_1l?S!rHW1S2L;ZJ1AE%M|-OAdD@MG2jviZ16;Pg9t*RW$n9a z%In_XbpWaZi$5PXs|w^sY8L?TJ_FTXLHGXDBsU&*hxbq1#C$gRDAgA8Q7^K2ZlHj75iQrj)d6opHcwEF?OL7+1E0WDT1`RgxdX)u15f)}7OjYkT zA?Cp~xS+5BQ)!Bcb#^zcDGvQ3z^!UEIDu<_X`e{l>S1X7{?-|3*fga+_Yubp_&Od* zt=BuVeBOqoOh~VS(|f=C?|cOd>PsD6^1A{&#Fc1r=du#HbG4q9!*&^lFZ|5=I5+=B zAJ_K^CvY(T+1x4+>e3S5D2y0sms^M@?1XL10lm|5du%0j5k|E?uZvMczB+StS|uiM z$Gx{NgUS!Qn(pecD=LdiZhdEWuF=$&4lPXH##KPz&4^7DX%qvtfFUL-ULa~hwSnVV zmj@tql4oTnA)K<);G6SDnE|4m-22{`y-uLW^{;q6|-D%ELHn3Q*KL5Mu7w+W0i{x>n@XD|0!>{j|svJ2i@Ot(0x{#o!%s1!VAMxQT~3y zDiHl4Jo*^$xEKPC7dn%3n9s>?#u|;cTmeIEsTU37o-e!6?(uU>2yMt$;rU`Zf`}ZL zX$Ce#+)%-y?3S6t=Q(<%UasdYyuy~>I2>{VM<3W@1Gk}CQcl%aLLq0)*jsyEG|L>@ zLgV62DRj2rdk&g~P+=$tfEJzMq^heMhDoX*6lX)}mcKZBmncDS$F z=~l1u=*>cb5=vhjAz}Ty=(9@MkNEau9DqkQo93P}~UB?IzWud!p+b z;e{aCzg1(w{z%o46!2i$r5oJUS(iy?4g6v+9~!+Qq?D_0P{Rj_dW} z2S$>)Y+)1Hqe$c0T~$@TFLYlo7%{9XzV*5Qo8{!6u#8bEXOe3DHrQn_EJwqfos!4T z>An1HVhz&WsXR_gS?FN(a9Ocxr)B~W1_r^3I#E&zmQ2QS5-%C$%j|?}G4~HzaZXcB zbeNBevK5#<+6(8UBN|q!n*WGpVY*-q4 z4t!DOaN~ZbUxhoF8P!AQxEH!W7hZnqvGXYNH9i z>*^o5Pa+rmJZ|ege)3J1AMYCK?)g>KeM=K^NJG`s{(gDcCjPeo*=qE?%;|HA26*d0 zG#86pl>rMKf#Zj&X`1(9FM7Z43(X1hLPr7ZwA=;lmVZn}5pa~WXLtOMXkuTU3kea=hx!%M zlDhvJ3Fz@7{N(B6A2=}=J!VHMl|uW^a;B)Wz2QwS%OFOfr4bhly$cr_yMJil)90FA z10(rM{2Yxsw{dH9pA`4?->=}sH!e^Ge-JAEEk{@=p|q<{*k*3r!{8)y{|vNqU*F%Kw2_gQPbEbLSWu7o>U z(t1yxhRo+a`yoWkq%Roc{VYxBJe2Y*Y(-Xm6NV#vIi-^{GLECagEVTd6k_jLYW74{XZ2#y{P=O*{vdqvq9zF*-+58ZysqIF?DL> z2#1O?0T@d|7sqUqCO*fa@rqyJ@hdXmj$k$?eaix8pisdMozgAMiZ(!>d$I^W=2L!nR}KNu&hhi(pbkav(f>R?jo*cV)q#A4 z&ijVr6D_!5sp`%Y6sW5J*f10p3GUuMLLvFTrK!8AW9D80A9_1Pu#y!vuRXUsJA2@}D{n2bogVVCt9+10Yt z@^Va{3A!>Gd=M6}Q*K}n_+^x*b&o?oLx0j9$XGd-1s(&6o<#M^>Kj}^2IatDtkR&; z{gcbP4@gV~wZknXT_5WB*Gd7n!*7+|cBRDOzDKD~$$E zWeoEi+wA$at}eUjjOc4^tYq5;ESDZ@@e4^wtfqLPYi2#L6@MB~lV3qa94pX{2&Aqf zpAb@FGfAUv0_Ghf`z0TfHDg~&x}j=A;gUh^sEB)H!#>(7sCsK3yjk)Es#pWU_lUP6 zk(~(>SPTMUCt_%;DH@2UVD-G3#X3o~VDKYt1No3RE1z zIt9^)-!g@Nfxcx>^4+O37*BXSFtbreG%b47llV|)qPTMh(SAd>jI3{dDNS`Up%6|= zWe<_&`O%(cxB_ip5z|tHu`8jCVN`&W9@547utI0H5>+N@>|4Pf1A4MNLnSiTi`*8S@&o=X5vTGpf}U~NDw zIHLC!+ab=kVN{d1sQz$`HvZH0ajqmmT2kbvKA|HVP8M2Ym=FEKlQ=#`98+v^nk%q-&<=_ ze`zYZsTnC$8$92>`Z`T=oNV>#ognEW-DW9kFL>RqHC&A}A^vtWd5eVI5c}l@620{6 zMzv${&eys+=iNg>%#Fq?OrO^5oW-YiCwb<#aMM|y)1G{S{3dykBIcsNf+ozW(%j!l zCJZ(>;|1J17Aj<&d;#qtMc=o^{JiK?*1wDXAdL=S?;|GQ*v5Pwuv$-#Dmy>GHK=i@ z>s5kOtIYhiDLMw%EDysv3azLz>y9Y~Zgf^|J&B!N43PICM%8RL7K!rDZN8J{Un&-Gb^t-~R57+X4-dPAVpL@`!UB<3h zDM`rXGt&Ut{(E{dQ3>tWnWJp-Ol$`nPhnr-I8*g{iBalx$q8p|bt(9dY+==Xf>! zE3b;UcH^{qU(qD_z{SE_ZaB++b)3XxKf1C!cvlL#5xQ+;7n)KARdtE~QY05PDVgI2 zhR{u$5Gmt@g(mc=5OiNbTkhoxL2_6Ju@6nY0Cd?S18|(Xp78MsX=gK!yTU?KQeSx) zojO%!8?_jB=p*~vrGr^Z!ppvTH2CVcQlitTS{U1N+0OYvHu_n16YYK$)_nG)9=NDh zD3Wj<*v;P%vZP;#KcmQD>60+0T8g;$H8&IqbSY~k+Wpw|X?IigUEWUwe_YF_3HfKE4U8g!%kccRh?wwEZ|Odkn3D#Hbx%2Qz~$>TJX5;`$ExsU1srJXv9DU;TQr zH?Dy`#%Fw<%m0QD7106QJhYP_I1{?L@52XXks<@#=ibo?eE}6Cpjs3uK=tZpoa}G7 zU!zTgZ-Gx+ROg=OLA*|RdFK3x$e@?V2q8p z94=|`Re0n^E?!fpa}ac&H>x=h(K%YBWB z+_IK(NOGCsWvcn`A1(B_8Sr!*cAhb!364xlcCSs-l_Sdp;g!A2ATAc&%=G{OYQ^MZ zPtaT_bDq%HT=lmZQ)!a6r`;+e%N`6x_Ve4o>I_6C9?Zy5q5ea!VmWxVKUIO~z~81N zyK?E!&{2z(F0wU_1HkG%0X{KyZK@8d2&i(P0CN@OPxNBeA&!ctB3`LWr-V9)eNbcp z_W+&q({Y4}b!e5T59-D%-wQmntGC_NeY)8|vc-!6Mb!&0Fi&R&h8avr&-WB2CfWP; znnOo%eNSP*@5D|Mv6Ui=f@Mi9O>iHc29XQ##g! zSPHlmxGsrsgID;HpUOe{N`YWnoKYmCPR0u7oq--~nL$1*#338w#Uz1J<@9Z!0xpHu z6vB7;IbaP14Pcg%?YGQIvZ{9vavz|_!w0mmQDm2b!>qDAOH6o~+IDvgb!cMX`k@i^ zuFze@Pm)KXioO!|6y%4g7CZO2TF)KYxyA9H{hMvaeG_)H2j-T{D~x_uKxL(=Gwtlz zIT&bSwbLE?xgz3ZDTdFM-W`L4_TpUiYDYpty5w#R*gee)`NSC6b#VU9*67TFFH9St z-uBgJTtW9O9~g371)@AD)Iv#O$_|s#kJ_EBcxI{ZR5a|{LoV(Rg$7#qf87?Pbw}d4 z^&oBt(~{?zsIi_4C=x^m!2iPadPPD<0Ck8h)vVZk$i#1BMpB)x=va6iS3;YTb9N>* z#>*hwtYY^7rEaQF-r!utwenBGPOO;UgiP#22`NpofN7p6HyAwX9c_tL)++V)!AB@| zM=>T}kcH}`i^|iocD*ED^o~*QNg0rY=svY&DGdm&>ZFtE8e;>C-Unb&=|SxIHw%SP zufr{EreU<^`_3uz`@~?~<3{Nlx2{yr5*2gX|Im6u99IJ568kaW)xd9rT%5tMXk5px z2x~)O?jNGGw8_m26)Oxp`&?xkBx?unprN-I?*cF&&x#V;-=J>URA$_7Z_2eR;OL^Q zLBZMM7Z$VWAW-x@5?;+-HAZ?G-G72r5}oGO`}UT<&0+a`h+ht0ifuwyk_Mv%&TF2v zev<#*-uO5w!H7d`&@*^tD?{DY1`W3OTIjZQT@bp{19K+SmTViCpys6_;e`fdO0b#p zQy(VFBfRVVKRmWlT(<|-MWMCl^Ii?TN_f1{?)dXn#|@)1o;a5<%O7*`ahbv~@LnZ0f&deLB9IaO}ySV!6|YN=eq~)-pXcakeV1>(;yzB%FAS zl|20xqBx`xm(VAR9gPArY*0Zl(~n4%hM+YvaC>TL-i4pB{<@$PJ4mLD^XlyNT@CE6 z7E7^Q>@n=d87^k;#OB>i>5g_E8rMOwPwU6sm$n*HrT%CGpqVhpu)4K_-KajQNnP`LR#n9J4$3Y%p1k6Gr=rmdDB10CGtkl?%~)l}s+wnDT&k5X^azb|WT)CxU>+3M3StSK z4L(I1FAKB_lcC6)?*#OZ>JRy3V7+ly_oYfjwfK7Kzl#V(Az}0i_F6AcE>HX9Cyv7n zP9U@GoiIaUT{&rj8Qx52Pg@!Hxll^bDVl8Ql#$u9Ae2VqrlxMg=-if~-PN}K4tlTo zq_Nh-o>Zz}Do0em%Hh`tu7FLz3&zY1?>G-9t5j)qJ(AwyamvGD-W6Wq-?^KP6kjMc z2o8{9?+xa?M<_dV1AP*%cFjNM7bS-PH-tvR*C87)?-lnC?l+wSzS*B^uY&d-jS#1A z$*e8`56PO6Q?1t=sXj^!$jMh&xLH_j-pizr+{!6tWUUAp@I*I1!CfSwDrymz~ajcTxCU4$6#nN?(Rw->&-tlqs!rtZxjkf+B zhC;~0r3zq_w=vYXaX3hGt}(E766&hC8oub(DB`ngND3|(PlP&b2b!F=<~fJYP|?oj ze_N%g{|)Hz`r+JwE@EeQe13(NuUZF?1gxAB_rf2J=PMMl9_aB)oAJ!VqUU zg17%o@`&H0W5WOyxlh1@;dF2B_kIBy&rNCPuQRhDM(~D900h?)W))ye#-QbQQETqy z>i^6sYZDr8TypBAF{EmJDf8jrV#AHzX~}K?eG5fRq{mJk=}Gf{$vvc~=HInG<*s_% z`WeE|A3H89_23$k*EIlh#0+BP{9iMHbt*F{%|KcyhZhvH7qd{P(pHeKnIx(vGW*eE zjAnEN`S~^(WC>XA_XoOy>8G-1jrROd&UiiZSJnYZ>-d+g3;XiBmnMwzH)yOleQ4$X z%S1kfCx$Ms$H+Z9Tm=es6OSDGY%C&b-% z;~S*x^btmZmAkEqLz!Oy+PuymsSu|xB_w5I$GlYz^+9{iyVm_B7V%tf*2=7D|8#|c zn${X6<=-v1cw&xEFzcpV@dTQK!5(x;4nm93!b9VnVwS!43{SSPNIAb_bSq59j<#e~ z&qqq9c1_F>GMf@IdF>)E=8x2cyWVAtS(L=if7w&p{ z9P$o07$MppQH)VM8ix}x5U^&q!FA8f?Ok|`QVz!h&Gjpqx6yejOGr(~8bf~(1U(R$ zZ>UOD6I#*41`o~F4}iT5LENq9;)Bb!0jN%jriEqYe{A6CA}z zb`omVT=KhtiNtO|mAic`(Vb#iL3_1f2I&g$fi(s-J$;7!l5k5;mmk{tWnE1$29e=X4g{4df7T1&e2`Qc}eS6*1*iN_8%@Q3CZo_hzLxnF$Z zY@)+S@XDdTLfC8XCT1noUsm`@yYi&L^0Q<^0fw(EV7miGcgL=u7y;^Fj7ec1?k>x; ztqdy&9q_F6^Xy|(;yT`7mS?u3`y_+?&XK4F={*^eJm~$kDUiDXsxTaYH<1ofmyNk^ zviM1G{FdZP$UL(C3qr&R?4gcJ^@B|Dd!ZF0JP<26reB7?0u?HKn&yWEp&Nt5-wuAl zVQ_i4{#6}-h>!$9h}Jd@t9(|t;dL73c(PqePY97CESGh?$}eI?V-o*JOB{YT%F8Ki z^_gQa?Y*1R#_^rwY*ZIlMU_g>TDcl}DwHZf6QrQ#vNVB1FI3#4s2x@6)W`LkDqdY> znB#_E1R%6IkW3;?wRrV>b;tJHx6NI3#sM9H7Nff1XnkC95&v2;<5M8cI#ADK;9{%Z zNVuh-H>FSRhCNeW21+>hz~C&$K`UVUf^8XVsbQXPy?-JZ_1G!)pD9-hB7KENRsr(@ zuWO<@c+=GY?@L8Cp>zgY!JU{gm?*);T%9yK;*pEbn4qY(2S9nG_{!d23Yy{m&2Wnh zx&)@lAkmt?2&SdgWJi!&r47PN=>sa_SmFTg8?A2*3(oW!nW^J^GD&8C{1vpuw{KCa zz{JuD-n1|5e}a9YztNkXP@5e=^!)`kyui0U6IXKPHJ9t?ic76hUZfN z$m4o{)q@0=6`Af7&!_Bt3EkaAJ;mTEO=Ng9)y)*y_Ks6u*%99(j$mPlNID-qP-O{s zz2iss#e`T@Zc*gqbT+d}2e)FZUZ~5%esjk)iR>|M?KjLEtn24P3_pH4s3P|rI7d%8 z-)2AuDixSZ&6Uiuk;nkTXj{2PV+mS8Uxn!V-{)3(ar~^pXi+kk`I$Wj)vut<2NL7z ztx6S;L*xjZ|9dIy3rlts=Zr#Cta>1<@m11#{$11^B*qIXya=)@?aL@RV}eg69euMo zd{(_8NsKG%g{lf<(3cMq$CqF~r>bNQO@V*NBhI78t|&tlIR=HvNMgz_k^B~_h=+2- zxfY+rb&UX=WE)wOK9GM*E2D&(pqAc?`a$D`O;g{%|W>BxJwiDV{qR~tl*SH zn}+V(GR;?d3ql-aUM>g1Uxq?o*8!&vAw!m!o@@X1!g=qLK7_Fni%c{r2(aRf4QM7+ z^2iIG-wjrWVx_SZ^@vW?GU>8fVQtshd{2v)4<}IHewi59p-4KVw`IPN5DPQjo0()u zsB(QSx&ga#EZHE+qCLIie2)usb=+ceJ1jT?jv!p*zl)}>W(mem`fg!=BU1}`fH2Ed z^Rm2MY7vqTO}r%YY(}29{-1@Nya3S2SU()0qr@-F51J-gHk#=O!~5AanB{<@LV}=L z)y^dF;|9E6_4;tX$=I^xnJ=NHn;>KdIfJ6y`yn`SVkV|zw59NHs2@&FIFsLoD=MNG zh+x?o$aegT?-{#b-iR~+E!9-jj&v5m_)6LFxOB$3dOA-GQcb~ns*1UypUB(-4RuJ{CdpR zqGlcK%jqpnJXxSc`P~^myCY0iDj3>>)`Qb=?fg0PGdLz6disUBp`dK`6qqMg)&ZEZ z$$dp2{++ZPC$wmuvs14>0d)r*odb{{8r|+Pieh*qE9E~V6ZbcsMRTf%SjW2OqH;wD z&qK(5ox3MP>mz%8!OE;hmk^WG@esS#YlqrlpZyk(`EOp{QW0-BwBk-YL-X|Nwh*8OYB0=OHNMCE|-@%lJ6L&Wb4 z&!~S2M3~T9X!h9z2zn;cisB@S6c#l4CZrZT-Ukgg&WLwyKlB;h8X#R3Lg6F?m+FK- z1VNjQBH#NpHj+5YL-Ja_N7d)?wzcm${#3hd3cQYVm+fq^U3%U5xcWIzZwp_$SpIrRjSjnU zP`7sVn6?hT-Pfh3T&*WhjA;E0hDD7O3SA65vnkl91HQ*Oyeyk8hp{fneYr2CiFJ6k zua))78VtVVlU)0r$WP0DB4)J*gH9(fYS*lNs(9(Y&zi|MUxdOOdi!5Wb%N=oeK)$k zCYU;e9eSzsmd|3d^JMf*lHpp5UoQK98W=xY*?jku|7%6^P~5{CHHJOM+ikj>J13LR znQ=MDAV-+34$mdbaVQfu#jhq4L{8q`U6=Eu2ULuv=7&NQlUbvbSx`%v%R{X^fKh@w z+tr1jci*}A(K8~Mk?u1rIkz4!@v!)hTKVFBL`<9vtx|&~tiOVpEsZaxD)rPr%;?Df z^`$oks^spJLb?!*CrPf2pJGVoUWl&q2-nKXmx6pX-dVyM*-4_ki+d?$yE0esX5{-i zYohG@z{amlOg0!FhV~JGwlx6^)8(LPDC^l|{v>M%`nOGb_go$VgFcwIA?Qckh=a3_ zsks5i53WC2bsAi>IiS){ISV$&k2$RYqSB^efA6j-wCrx;`I~^g^y$1#aRV{shrznG zZTeo?SGa)&gRw(8JBRoP{PN{!D21j7$#YZsd$11JPZAfVz5Mq-M-K{DiropBl+Cw6 z#am%L>|`b!nbV|=G8dxtFq$XlQ>nyys~O$@7+KyAAuG!ZoHZK`4ucsnu`7Rg?@xVX z(q7+B#P@Ch-UM39W2VEr+r8GuvsUk457t#-;|>GQ$1z$MtMJR?!jf?Y<9V#hkAJAN zp0XBfSvjomxjRnKcQ@p;MFudgTukVJ9eR)auS+6LL6p`Qy5Dwbo7J;v!O|sP0U6B+ zpHS>x7$Q5( zPWe%3Ip`f5jw^@L^|~0zho(Y9!*nRWi;;iMBZZMgKfZgMHmcYN=B=-ZWj}EHybwk=wc2@W!}FbC4FAzq{8zJnpT{`5ecRDdv8(LkW?C zr&-9w@M%m2lokA;+YQ*ya9gVC@fyR z!JG0+3%#Yx#2;`q5GD4xzsK3S8PuWy=3qiYhZP?H&a5y2;6v$!rfzQKk%DCQJPJs9 zIYV&>e7Z;=2`9ZR2*xW=ldR-I3r}L{5*15kel*iTyxP~ zx!iVO!Wyii+78@s1#AZ7dDP@C)WLBUa3wOPdI69okPE7G+}elj7#C=`!z_r-PI+Cl(nZ4r5?r6US2h0(Y_(`#8PHf{t;){hDS?-W8(!;jSOP7KRe|_{q={&=p8=gb#{p1ibCHW+W&* z48{XoGWf|fwk9*17n|6MMPReL$0o6iOG`3UTrz+&h%^O{GU;|4ZVTa@Eln zSZF7WxYgbglrY|#%4*D42~oSxC17iVjX7f%F4h2@W9NbZ+bls?o3uKc;Q>3 z8J#l*2_8dLaxfeo12kPnQl@=I^c1ZqvM;*`I!tDT0g1v)f;`u=X zv@Hl(5d-x*t@6#W>>I6~ehAu$RdZhROlX}h&!;2IRtXvc7mS?!CNIfnH)%^iUo8=I zD)(Z+be#S$tuvre=M_o&oP6g~F$^k7|LgHFCgnvSauHNUXyv zLtS)nEp`+;8dY1S$YU)HOB?_*+_p{@$8Zj}#ZcV4buo~)# z3zkzTUy315F#R&KFXici|64OLb^B-+qC-tWR~`}I?2{%N)9+#0B7jh*4W2AOxhvX!%b z9_Y#}!DFw5Sf2=&xHNhvNIL_)9vK{GK+=Z%`J}(w(j#uhnkKor{0zr`Yab7I?EKuA zK5IqE_`(%98AO^^!JQ?}6|lNjaxKCN%bm5}0)D{ozzOGg5OJ9(DU!Qf=q7KM=q-Qv ziWKe$z;eC0wo@zMS^?}y*7DLE-{cW!0IZLD_{!uF<^`#P4Yg62M}@Upln;oYdY+7C zob12T?bG?=fvFysj}y+i58jM@-1XEvVAdt9wo-mr8pT*=VM_H8eIP3#=f$(#_YoTmRUfP1C8WJL#X${b%QJLA#B~HLm|7@I@`N z%>$=ku^IJVGWUUq@2D`D46hAGx8uH@YljEG3AO^fT?O2z1AtuQJ$x{v_>fLk6%u6z z;`FJ!*FE4^nc2wCfv!OnCQ^lO<|HjNyLW%kouP)jyI{tH5J zia145ZQMQ$|4EB>;dbWMBKC0q-ez#=;J%SPG!sV6{50|{>h>1;0gE<$$S-O1phi$B z?n;TzK+b%Z?06Md1NM<#dJlYQ*i5fvN+wv+Ar3idV9(*u!M{~v1>8ysu0WEM4J;8w zZxwW~`0iczVyO$}yKsY*XWM^AyLljCgBE!Z{KrsW_znFm^8N>E(?67H`Xv4H-G5l+ z%C?%*g$`}@9f$Ocx>04t3yMykM5}^a8F{_vO&v*wUu+sMe)Mzz*Wujk;h>h<7d|F5 z9wPc!iy!6BRi8|nPwg>vmfd-vXj48yyo@b%paqRV4cB(=**{`;i%U1=E6D#-hyU}C zx2+Q9f2f~48_$F$e#_nrwbA8Vx$%ukB5f=#+n^IIRRh;y5+qU4>!y`~x)LDxGucod z^vK^iizq~I{JcQS-uht(l~{%p`zSXKgNjmQk_8Q)zc;M zn?;*GBJaT*#-cymJfOka2~>rpxr%5^XQRv5&dbzYIWzn9)3@OT;Dh4ad*1(4fXt4F zM1XfCDKRIaW`!GDS34UVFA7BoI}}=D*G12g%mSMEFmeFsBO4zZ2n}U^fHZ`P=Gi_` zHhNXhhL~FyZPEfiP1iZ;;H44Je)h_V=Vv;{OrRc(uK9f9q~xk7D$2#Rer}u0iM)`4 zo^{%aIyKh%~WGMoYk>!UC3hs#(qeQVkb82thL!!RLM%34Ffi}M_nY|t&eSxc8r zoHzM@QzI{oI%(>QrNMzZAi@ABt56c1Y5PCgJ8CK=c+O-0ZI?ztbBsCLyOf9Mw8&U- z2Rl!Y2U8*u_T2^8dbWS_5)Ft}QT(_h|Byc>=}9?e+OUfOLZxfJ&y^R&Z7tNAv7o>p zclv7tsIEZlKQ4?MjOCA_(9pU6IEKgnOcb==$M^X2<1T@h}LEbv5h4TM^~o)unB97#B5zCCY}Gk zwJlnEqv*fo2|U9K6EVlXw%@E&r70n@t*2)Z;cxvLd$Nne4qJ(Ig1;BZ`WFbq)!_d= zO!T^(7H*3U*Vd%6*5PoV<~IgI(-&{`GhWTTmjk%&I)UK7HkzQjmELvC% z{n3K|w$zVjozQx`r^UA(iN^hShyTA}^{LO`eyd<*Yp1a@w{gNr>a2)9E^G*+U+DbF zuX`h(dpGi{*b#*Xj(rK)K|Fdd>*Js)J5B~DVtEjUyK>w7?R_`e%&xz z*eUa$;lrUNzS=}KIMJLbOGyd$gqGnmG0@tPO+lv|qmqnm{fb{Ogr()IiIAuNF6tx{ z74GXkg1dvNj}w651dvr_ZL#0YSzqiX;&x6SE2T1IM=e6>QN*7^TNu%m)?g7>cxJg_ zrNOA@1l0BS+tc&HO+ntiL3VymsYW>LOL%sYe6}woCG;h@J!-k)#uN!eLyK+zSygUR z87QYd&{S{M2H3sS4n)KBf)SW9J=Ec;m<6^mMiuTLl4mH!h8D$R!#WX zE)0B{50X%@_es!XtXvn|p6wj=`(V- zp7fD|Ha$TZ-Yf(3TXI6HLZrD?kv4TrALh;0n3u!->Vratjl|CMTP<1d zPg!V@ULd6&Rr2^;8Kz^VlXIsoE4yn2LnVm6tqA1vh+H>wyRb})bo9|nWS`3o*2ydt;x)0ppkZa zka=qI4`lJc%J=C2#QFPhMe7~7&s)nePa4r@y=w$twqDgw}dm73}T#S059LGN9&o3EZLxwSbkpOJ23 z*G?pJBi8o+6>ouBH@5~>(FAREnGP(w?o(enoE@~ z@k-{>-E|g-amC7jg9dy4(RNsuSuWj{uH+YQV{R-j{027fK5D1GMszLf@Zgu4=NFtK z$(9L2&kU{g-P@chFFyM)QDYc0e$<|~`>5e;yJI7+zZ)(_VLiBuf+On-o&!eo|8e#8 zaV_ru|ITsKaocf|5bZ+|ME5tl4k3zm=+x+LlA20Qx1*a) zQbTHK)zs9wUu&(_w$^U0UFY`>=bZ23_n*fD+qLU@U+?$x^?W^_&(~H)Uxv85csIz@ z1|?Hx@o8||90}}-zpnF6X$q^h;m-gOWrKMJrmgH}yOsUuk_D=(89SOeHN90@^_Ck_ zo|bdwkOSJeGAR^@D}&cJf_Kto_a(_i5^ zefECvG;0Apy;u?WqQ8g(2GIocT$W5t{!Ue#ya%f7Uwv=`avOND`k-_)V5d{&8_DJN=8O$UlAu1Be#gZ4Ry)`h zXZgAt{s?nT`pUZKaDOMKmb^&zMyolDQui30DQGE$dYe?gITA}yHneh#z(&D;F&!oB zmNFLA(h7Y3p7HaVYW=HYB7E+#vU2nYlixuSt?R#|&adr^@VE;5o;7nhq;jzzvuE1| zWYuEC2P>m(4%X*RfXvg_+ot2?+KQx;ol$$(C2Z8|%J2O&d;@!A{2H%_bc5lYPEgmO zE8*oMo#puem!F0G6!}h{PWg0#ul=(-{>iW>Myxs=)Zjg+wOfR<|0`^BvY388dFbWJ z{YyIibFzGFa>ypwTN zErBk{WmzLh1uO(aft8x9y3|R$9kk|5)XF}8tY*H;u08szIMufe(9QkanF()Rbzypb z>$I|nS~W;|n#`Pm7j z(fUO5jqI}bPh#FpEKi@VZ?efM+%3RJyhQ8j?aCv9_}#S8gjbYSHFOWCx}0}MjF;cLKz0uDH|ntU4Q=OvDmA(Ot<8GP zVAawZIa#R&K1`AFbdbthC)0akU9|ZKSEJ?dHH?r{#1`w52RLcyz3@n0lBymkMEcL# zkdg2WYT731*oXPpzk-@SRVpY{&(1GSWtX=b1E*5e z*beRr;axAH-wz27>y0K^+EJ!8if9|Z1*Zo%+j?gc?2&EM5=~bXH5W6Qgr%+oTJZh3ZM51#dTYq7^3_ zU;d?h1mFV1FSB^LToh79?{h0AQYJxF-VIjbrLs}_iF6BM15JtKU+gl3-==JLOiJQ( z4Sp`%sOdo<78WOs3o3!1_0uN7#2XibE>-%WQ0qus;?WBAR{BHm zHAi?9{m*^wad@^r^w;fA0S6<3Bq+mRmt&=dpGZ_iA%RXq|Hg^sj$Em!R1sl{r6R@e~S8sq-Y7kpjTME|FyTOe|&GG6X3{>A&r4AOmFn!_nWBiAsVFEQa2V z-Ng?wde7o5rz6UCaA>l;?=265lX|^v46!c$m5xmdV*oRe5Bclk1tRn+S5|;Js~H~! za%^eHE1lMArsoc(a@fe+wHh`7@vRoGTW(q#@ZksSOh?ySW7_@WCIR9gg<}9EQo=;wGhB=cO_mAJ_)w@oS z(itqjKqY+7pyf@?XtiZ94X%B6vaolB5D3dq@?K)Lx2K2FaRLnl6^KrPVRnTj;ir*I z+pe6lO2k}UjuX#ALsO@na^#y=|I>)MY!>ux?%+Pb0)CNT8^KD!gZs2sk)nO+hVl3m zjBBq8&zW8XGR&RlD!%o6_p{6W)MqnO9KXoc#44GTnG;Wwk`g{_2lX6?QEEs&HUw%& zVl5KWlFzo*28G#T+W1w_qj`knrLn_tz;MVeyP9!Jk23j%`fc%n_@}QHQ&atfm3V3%`I$X}8RN)j!Cn4CjQyzh`$P>mn<8jXFdD+y`#ui0 z@K2}ji~#3x;9@01uu{YZ)qy^*aLd3>R2($@LxzpgK2)udOP!S|`k^!dsJinpLbz#1 z+^`j2-;tR(@E!znQv`PD(-zbfKvH+qZcFrhU~ttQF1zqgW7b29v?1IQc3b`=amHdL zVJ2-*bvFAQpuC}hqv34g)`uGAM`l1equM3kX?6j}!`fm+Q@S!pz@r?=j%zO1s)k-l z`E0&w`8pB?9AA(pnK~%21qtrsp#LR7W)u`YU<_Q5sN1=bQB-ulSE5w{Sf!vH2iVbR z`E>5XR%e)l6|7o1T}Xc~6D!9msnx5`0WP=@gxqIW#nRZIF9cLWE6~!Ki~87c7zXeF zKJqY6y8o-c_VMGwfKFF;6=`5+^1=PyKnX3c1H8d6(GZC3=Zc>z861Lv zvYoPimlFO{FevqAO$+$^zSWY0CBPFl@Z8t;ipdz^S;-J82%ies3EY`1PyrEpHN+nz7@wv2Sd5!fP=n#_|xna~#q!%>CSj5~f zPkPbvgqF2(lPVZ-ga^GIm$h$b!4tYDtiWs@m#k2gzpmeIad`%~+E8jJ8?{l~8}*l| zHNU*EAa(eu?yVNYSi#M+OnQZlQCiExA`>SFEBEwfvV6;Xb@FJDcm^r^zGil>b_-O8 zpW9117xl{{XG*O6-j#}!O>|OH&nvI)LmCsd#dyD&%n|KBsNQvi7Qs>w$k|>(%*rae za5d4u0TZiK0V*JBj^40{1heuY3-9=RM(=$@(+O+qT)nnP-Q69cuy*YI_gbs`^F0Y@ zAMZDQj!i}_Q7lM#UC~-M;2Fi&7@&+9nERs;X259V1zqdhLu(tVJ!p-4CM$!X4Ik+w z!q)y&T&I($MAP?aS5704et0$L-d~&&Ip6mh6N#3_Tw|6)i;^x}Je^`0{#Yz}LDP-V z7>UT7`y$UFz1r2Ovng%hHR&<>)eGo5+h=@ucNY-r8bB`Bt z^?Wl-8+M?#?S93c5i~~zidw^*af?S>cIht@YuDbpT0aFCUv)rHXs>0B47@CRMAtmq zwYR=|=`~k0ay-)kA1>K%?~&c;|EcSK+k_=^t9$Q<@LM(&F_(@}L|Y^s)H{3CG-o)o+Yhl`e{w&>Ul-bXtet5CHN&EF`?ho zyIIIh#zklj;HRYkSb>b{4PDkn>`sQ&$Ed4fno0c?=ED6c78j}1FYuXj~+igCcgM*cncLZy+mGU_))?F8nSA$N*AyB~){dA*`)qZ?ylxL|i)?UMTT zLwETx{Foi5m+(jTh7+aUZxgRa?AmHk8aX;JN;=ogJOirfS+$Dy;ei9J374K=rrMmo zK@qQ95M{|_s$0ibz%@G=K4k6TDHZQJ9t;h3U7jUke74b_AW(Z*i*fzvX^?RY3SaCk zyOsZ=#Ura8uKx4$>~ruz<5ju$X!1iYA@N~(cLEBkyed1Q<)67$n)FvqOE;w-#{FC& zJ(^aqdTZAx^#h}>7-#71)pO5I)E;4~`qJr1%i#$K2H=674m<8#f;LT9$0o?Ez11^M ziJfVge}EWqFk^|az$@XG6Qzt;M%iw*31SzmcDn>Env)xj#y$-*?15@{OMF7C;CH&G zI9AcUtmT+@#Dt9BNvWWIGL-uCdugogUg{p(B9Ohp?Wf2Qk2g|yG=Q_E{7Pzp=sjkp35 zG$P=UfQ>E&lg%~sbR$w~@3v76XSqJ4VE2ozMB-#zsB9Q$eULqW&-e@aIck>(Oz5;c zgZ|n5zo5nkMB7O>3uCf3TRaLA7#*Kv!S4%f^xmy2e3d@2;E;+||p!J-*E_ zh1rKEBD?&3vp|?obRp|^#YL#k85(mR*{;<)9(P^ZJ6@m4D&6a)K90DSzW}reqyXs# z5rDRSlUNslXHu6G!BQ(CNo|JN&=Jkgyro(ZX0U%jj^fg`PS(^UwG*r&_ti4qJo-jp z#J2)Lr!dnUF75!Y9wi2y0m__**aTBZ+Ia(7V6(-yWklajw%4c7DCrs$lR!C2!cL}7}!!iL=sR_IrVy5Z1p1tKI z&d0_jtgdd#AsjF9v_nME`wCXG%^OPQJ;+|Yex&BO`+*aK7oTS36#1Ud^%B7Y+gu(@ z`8_%n32+KTc57HX_f5OK`$ZC#pW; zOatXhKCuv5RBc_Yb;w>jpmK8UH~4lenI{2 zHPv~APoji{a{`(AU3P{7G`w(R*~n>NerxX(EX-syAlG|(2)OpV_aD=yAPrvNe!v`Acv)!6OXiUpp6I<$gHAy1 zLG%Uu+6Q}Hs9Iy{!IjujE#D^!>TRmQDe5V~b=Lp@4)xX>tsAy;;_E75=j~%p4KiYg z*w<(C0fyu2gPK-YE|ko;7= zMzjafXltW&Bc~2BkEAm&Ohoo$47>Jto6i#*hcbD3>FFO5GI*K@V6+2Kq%Tn%S^>sZ z9RFN<9A!|r5~J_Eg_5DFrS>SS!C^1iW#6JOup@P_CDISzmQ!K&Yj7W8F%4wl$v!`k zzmqCrP0(gFk3&JU$@gdd0>ms}G==LFD5iVNK1A#eb7crs!ykckiwhR5u-%G1?evgf z?mU*iXayfAkR#Vu27+G-0MCiyh7`W(z|R!jYz_jkt%Jw@>3Ou*?v3Vg zCWQkoU$atezIK9Sns4DDdpCK;AZYMDpb!EF7jP85o5x=>+xt8ldE}Wt-Bc>ozqQg0 zCFgqHwgkBJ(4rDBY4zLul%O(AbHPKrZF!+n*S!A=u-OuRL2?ePtfbJi{yJXj`k()v!R8>* zGT|SPah5o*r7OlzvwhSWY}4)8s+Eyzk|qa3iaQ+ym_Jhy=ITIzjnht0jWHl$9*jyj zA-f_4Ro2QwdbTp$0E2439Qi*TG`D{YR^d^Nv?<`ASg;qZN@aV#Far(?q+y!$W0+-j$CCu}0n zKwCg2m2CT9+9@}Go2jyDeNaL^nms5;@uXnfu7i_4Y7gi%(y+r3E)L+1q~kp%Czp(k zH~l%mln6x1V++_y2A`&s<-aNaQpo*;AJ}rDpaC;oS3Rx7In1N6v5YCiOTP;(yQ&WT zFlR+jmBUrv?2a$OT9^9A6(R98ueDYU9Vo7z(Q9{l2cCt)RZ7@myOJbyJRN7J`%x64 zbg}ZH)i$o+Jtl8@5nptMBz$=TL&j2o z$`8D;lhi@{({^twYOJI)Xyiz0YN-6`Wqyo?H;zSTP`2;`|Iozx;^&Qs_jA!4^N{@fh!`P zRSvYsNoQI0odY{gofI68PCeSTb<%#D{x}+C22sjk$u*JFfuxO^kzzstLkPd?F4qSG zTlcNfJlrG~2{l1Q=9T>`3%p}aq;ztRf}_Q<(w^R(zA5%?1ivHdwpl|wUiPggTb+0L z^E{pGiO$AJb*aUKS>~p~-C&kFlRv4!xv4z~Ko3UJ^l#_KKoS@(NcI+xt=oiM)z$ir7V>kNNLJyrad z|ArD@?DRYIp!2U>ryIFV+ta*tmuD|)OBw8_lZ7ZTe*xD%VY7ni45;|W7%*zSluZ%4 z#G{K56NTVu_iT%R%*ctEy`gf$YpBuY-SyRdc8aV0xa%2PoLG>UikMxyxm&dnv#=&S zTXRcKkF2~2T6bOD0C?pENuRRFf*bkNz}*{Fr{EAVkIFoP$jfT1%-4c*u3C{^b}3Yi)Jym1gfriK6T z3JS}(|DdF$iMc8#snR%kPk5CPN><-C<)%g!U;thuR%e>H1_ z1bTsC5F1!3>^*e0mn1Kd;O%V~?a7<_y-fQmF3LQt=l`s*VLE;))wmo=9c@?1iP_*) z^>qsbT?b&QxCrwFo;daIVT(RsZe?sDAEJZkV{jBlBPHa z@pixBL)6SV*>PBMsI5iiGdGA!=?k+W4<}ayzXbowMD{#-Hxr<1nNHKlPGt)*A6^H6 zGrK{9dv~teKFNcot_Brur5;A&@YWvPc^5Q1*s2CV48ivueR4FMk6}%Fm^6js!5up% z#~>C(Mm;vBfek(kIy<{{ba&(0?%~TLK^vkhHGEGp{>)u8mQ5>jP*Pzt$)3xyl+=(; z5y80{4E;?#_EGD;Kou$0;wzmmg7yN#i~+|dI!TMZN89;;b%Kll9o)UP7^?6 z;E&giB~;{(`$Kg~!rtkbT5ikMn1hn{uHY+m)E54YL5f0gMC6A!nBzWu$Zz~dP+qcy zGw6xSH{A**?ioXm>$lL0c8y1?;f#yRoH}i%{gog!iLkC@@L@QEe>W1o|LgJWamYj{ z(+j;CV8g%xrqhDD?SryP8+e2=f=&W8L@tiPH&fRjErfa1MBK zJw^<2!vv@sJ(s6<)1yr^e&;+IHM`OM$d;;NfKOtT8m&Ixgt2iKHh?R)X!pmC-ue?Q zXRBmJjk~~B0zF0?yt*3HJPCxADu383IOD?|$PlWy&8!?qRWpf}HAPH&i1!1S5kTg* zm;9&GZ+v!qs@gLAA12my>IxwL&~iKQJ;LUQ&k+#<4eXgdZ}MI+TBU*I$4_mmtoGzn zzGr>XOsK;^4Bvp-`h_kl986sytA|Knvlj;Gq<@26+Q%2-nX=|0Ct=#?e;4ch?`6;V zd}*5e_8D^dhmHOSPe%?G9zJr%-2poOsa6C&Z!H=+#%QvLwgx;b^`9YdPU%q(K!^hv zYxfmMW;IF9BKfMRg(V_9INnljP#+iB13QRP6)0Or`7gn=BKcii4aIlyhi(cOkPevq zld4fwZ1lJ%!ZN!1`gbwTnN+Y+Z~{<7x9w%!zV2g8172Qm zs?>>cQLIv?*;4v>$!eFqAD$$?YmM^&FE|2FV_wwJu(q>(B5wf76=$Pgk`f?I9{s7z z+SZar8TP(?otKjgX27jWE5Vd{O*_tTt3zZ0mwel0+=>w|3r~@?4GUImyzw2)J6nx| zU5qSd0&~Ojfw#v6Adwi^0nF~xEVId5r*LqB8G=7%Bj?{0A+Q!k7mKy95;E$NKXmhAev;B>MO?U;iHO*tmVkSHKv2;UW$D; zStq+UUed)#uD`df^%bem+Cp18j?Qo)SqlBfDcynljOFEBTd@lVCT#J57Fm)re6i8q=Y#n;J zn>EPT*s;mXFss;w`_6bn{2vS=m?c9TY)Rrv3J<`17PzVR-FqukghJ*4{WTo>>_N>X zZ;ub_)izq7^rDlK(Csu|ES(FNHDHA z%Ww{g+zpi(78S*e@`fXDpT~gDaQ@>!a3+jU2TZaF81Cufyg+)o^)_IASL><`rP_I3 zyQfSB1LRTdV^lDid(ewf!j2N+EF#BCz~0)B4zgkbWd#rYK)aD%n+@=oo4YEuZ# zM|UvF1>M-NNqy?X6Lw__P+=2NFrr=UE#r7w{0HVoTqz_fQ|qDcbydGXrPI=&TfF&r zS!RyVyT&0pUlH5qgpvw|J=c8-+5iY2j~ch?GN%}$U`Lz{Xv6C;%8saxy@_g}t&BxL%;Rxr za%&KIwu0;2PG1_)__WZ%+U2%w*_m`*9g$xe$=3Iw)bnBpXfpf9 z)MhLxpO}*y&uz%e8s>=;aXt~t?`UGcUdB)y0OzM6uyL*i@ z+YDcVXM-nLsmJxr$C9vYQ)m|2Xd#{z{3TE#|KVh)j>=_$`F2}1kjDfAC`fXX?Ffp# zvGbCL&SsNsmrjsIY!_HZ8QnX4>Q(n<^^{B~5ok7TjI{>T39Z4EC6|a7nRJkQnQ!sh0?%0ruZfvV?-`m+Q{nn>V1vsJD?@O=m_EGzm0YPmymaM~ZnXzj#Qedn#Y%7lAazJJfqb4|pI))_~f zWxS}g)`wRC@1d!^|9(#~<_`qh=K#WOfb8i!N+}F8`IS?~Q?7t|jR9*JiY~Fn z7lLtsG%c)AjfuB^m&m_#kUgZ5m6dWB+cHG~U1c0ps~5j>Zsv90{76BM8rvBe5dwBm z_+}WR-d|tsC=OxFCi+6MCeSCAQIp?+)zf}BgL~BuKLP(f@sdv7H81ey9zTWSb0^u< ztM#K*2xDg7HGtdN(NH>MA{D9Hx3=`*vM@powi(PAqpqdNjgRv>s2|LsfzaCkA-hVO zcy5IZ(fCE#BpbhOGk}pj0P!(o&({An9t-g?8iF-r-ekr&XVNNLuZ){0J4d=2^G+^O zE`YaepTP_ur>}cm6D@Cnnf@@|9zy zT$-{kufaqC4%T7FLVNFC$oss%NMR@2q{*z};VA&uuZbkQd=^020T95*3e67pYDRVu zZ=Op?Sz#qe_(gxqm~PQ~A(QfFR=pzSs@Db%B3~2X+4Q!Fok5yF77ak~tl-E>ni6Ow zs+7UXjHf;x*(%ucrqB8x4+pb)QMh35)|V1%4Fkk+?QNUqMio9~G^pswR^91ga!kvM zfU!FyBDn3~C4Jvotbp|hu?LpK1$3FwCaJO;-|pPMu&Z@a+&ZZN;l2&xJ<6P~&j52L z5B=6Qe>{2dIJX#O^uV=?x^(f=cRH{5;AcYF$FBQ9z2i1KKu$gn|5vVjI;1zZTNQ-p zI@sXud*ZvM32$pg_J62Pi}ro?)aKMRj3%2hk_k`#nrg*fRA9mYaPN;}$-hE??~ZMM z{Ou}(^r^+@IAAbm-NMm<21pr~zn8logpxd*o+VsH{wKM*DP%)5kUjvKPT@tx3tXiR z0F4hrOZN)T|-J!f@^hXsb7Z?w97(&YEbxYy=>t*`$LdFhXl9sp|#;0ZpXYFUi-3!Adgx zOmJ$S1H$XU5amD7q5U${3sDhtny)7RhJH=VCP7t9I(+Kr2@!#_677f5YjXl+HnkbGWr1gervrVS zwIxh*W=8(m$tltGiUiEZ%dLB|W64I*1})twCsSH{&VuP@6^qD6vWVEjft@)I&aSWj z0fFD4v5#{zXYa>B?#f9U0$o2ag0ifVx&g(B>!_AZi4qM&5!g>#3)=a5&do*N1Q&UA zK%4>EQ()0Tf9edct`GXAVqkrxbcZcwk;l2<1$c~}pnrQio)1^z-ZR=(vTQNxNO9uU zM)QuJtlcrUEC6pXvQiLNvdSc7=jALVsOBe6Ex}#3dH=NIZrTqSHdP_aHAWqMnlyrmoi84MEUmW@ z#u+;aoZ$15Gi6P7r_hWe=ij;pX#Y%g(>>JGxIWb#_8_)}xz{uq^;uD7raRW0)tTEQ z4Ve>BzJo%W@#C&z6*>C zvArCDOKnHlR`vpcZ9O(16~+xkQI-sZ(O1>O=Qxy22dzzXg>CSoecD|PG*Cr@(n9bQ;%iUh{ZqFjpQ8IZ7>k}L39)KJBDuH*#c&#qE7xl7GR~TG;?Q}I)W3z1 zYyO@wf15$OS+WDJ4_8l4fR|eB&Bx3M!>S2NJ#-0x6x};5cEv|I6#9A^P3(Vaj$-Eb zjjvMh>W+Uo^u&c&Z?idmigFOxSlwrJdMAQNpiS66f*J{}nz-@b#df2;&%=1NZec3` zhd0KUpw^%p5x94u{$~L$_v|luu8}9m+eT&V!Uk1YxlEy2;nNVw02VobTa9D;a3a7H z%&Jm=GGvyDv(?yUQ_T|vr)NCQEZY7zh3^EfSM0I>HEPuA^4lu0*tvQzFD&sjwAe0I zt5M9Xi_1AM2lu0bTZhhp6%IyI^UHmw` z$5=(XbKu3V+tfC~RcIA&;wUw4$|R2PlU-j|@&5j8v!WpJ?)#`w*#0-3OZq>Gn;RvR z(NR&JZ}hIt?nzP#vXVPT;I%HjC4_x~(M$Irj!@JG=4R7kG^_Y1&Tni2AS8@@CU`ND zyc{%>ocmUC9#-Nz@x{r!D5|0ZC)QYEJl1gXpiOj>H-L!hDaI6klmadv%q;GqiHgpj z_W}QRRq{)z>)la<@vi~(k*Azd?C&XsC7<=93=dhSxFFyeD(?f!%CbSaFa`w3l zITVxz64?$IbfFr+{j>Yw0H{|>lUY_smgs68gPGF5+UP0l*ObJmtQvL&*15U{&_$-s zJnVgGSVX7>LjhbsVeA2hcpqTPH=e&Yx|53-Omt`aan402zQwIZO^PrFnyYMkcNQ?a z9e2|no z4=~5S0*Jo^H9DkNwBaLEdYR6|hT1-%Czt1Xj8)@M^`K-HpzB|1X|z>*nc!4%*5>z+=q}9L-CqDdpDvKrog#mNNxiCR zF4wf0yo}%4Exc2-*{3u-`@CSV$kC@tm=ALVoE3Pm@SZ!Y?6H zg-@!}%W-$`-_kyu?o=hfEM@;#tJIDG5dFKMO&$&ECa5+CVj>ex%W>U{pd%3)YmlTd z6yNTNRUrv+^Sj)FL_?O}G7^`Og&?c&_zAg^+Nh&_2lklNx*@f(rk{;S9d3QUY^_7D zhX<#es>UV7Xx06&Ll*#RZwx44wtj1P!H{Mg3Gzm!A!Ho;8|!)1B6?2K10HY_k;87Pt>pxUZ zdeU+)CE+iky|&)_2r~s-3;%9#8YmI6ZLJWnJM9KXulEd0g#B-%2fMPh8u27h9nKt= z?4%AU-wB#Aey4+&u)rywr9Li#g)(akQ_W|j3yi_o_nqKMZ4@PHjN%UQe#!eQ*wY#J zxFbNyRLc7STP$4V$_OXIQjx}TUW-Q8KB|IXmwD@{Tmq9PmFw`OTdP%(7_ms<6ke#y zkpWdIwDd%%#D;hOz!|o9IvGfMc4cIQwv>40BxtvW3(iE`V1JvP)CPZRySG?`>%z_d zap6NE1?s`qp5J-TR3{v!eAC7= z<#1t6G^-0?JbHFP!(o6DMs+meWB{3SrHV&SoWg)5gS$X zO+?7B%eS#VTG{)llb^xvau~v)Wjam4XL_RnybJgH=KUlu;K_Jw8lO4HZHZyaBjDGc z|87;F|CRwd8jqW)s^>6{-9Q5-5Hqk623#lycBKV@g3G1U*DPDTiuVsB26>s`8C?(y zV1Hp<(CQFmu2-TBkIzQbC%8MBBCfJqVoZSYc-5Z`M8N#j0vP@;Xx+;{WS;0z_W#XP z`9C3_%~vhlwpmU}w|Y7ZpimE#w6%6L)DugVUz20YzgFB4&8MG@k4-7vxNy?Hro;bU z&!gc1iyej?FkN_Q*QL)S8!~^QFos3d_l*bw;ZV};mIO>Jqa}Ow@0OP#*_Q1r&(2A< z5(_0JZ}a6yWE+yI9$BDd$}y*@zQcTqwKW7F3}2_&e-zP}7UlB4CUV}g02&eK=Y~;; zs+2#a3n4*<-{Z0884S)BFJWh0QrbS>?hLS7@F}nx>l3=o$N7pm}@LAFn)jk3nbDG{xH^nH8~P1 z;b6qFE73l0K2?_3+j4?6Yk#AkAe-PN(7d9pj1lX~>3KUDNivNr#Mz}hh5X>9ukNQD z`T*!sG#7nqF>KI`-$w(9P1Q^9+tyJ=N@Zui&)503z7a3VmL`MHN1eAKl0pn4a%ks!4^<<8%ovo?FVxiX({&!)6|?V&0B&8PUDyZ?JEM zw>mu3cnI3^W99Afp$X$N8d8#{`Or}kFQCg3m|wJRMeP7aTi!fzKe04BzChiY)b}7U z@e1ms7umH$iD{t$=}iYX!uhWm3u_;T8P!HOq*wlET&8zZw==mY*K6F@jcri*?swtZ zPUzawKT%SlfWD}$FQZ({0ddTHx5tk7p?(km_5oiWe)i(Hojv8+U@g3m@#tAmN1A`{OXp&}ib!rEdfzCxk zTs1y6&ZX&HrIH;(n$dJ%`2w1cv(yP)aDYulLn`0tTKudlCjY%j!@0ha5iI)Pj%N8tDkpNCVPOW|i_pUT`z6g~^!`#NgN-pbwUT4l;{dSfp9mJHg&zo{|EVLBPi!c-aw z_Mn4FvXTetLZVzt?&L%4AJlyQdxl`sh$+`{CADA_|G2yy?2CLM8U+o5Gd&rcVQ!3% zx^lK+A{T7~uuC*w{re@|ib?d*1k`78$BepY;rXO8OYI+X3N$yxw3>nEJ`x4Ccj!(J zZ{g$4#?@{=6Qgd1?vg^YkKN6(Xz$w4m|ZY&QR7%X!jV9qjsKUJuxf5_Qi1S^%ucl! zuCanIjL(~nCV2r$olj!uO^Opem3oQ1$jW3QtDm0lf5B9QW#U$#Mi*l|ICIK>#fbj| zw$AwZ8F{KgehOqP>&H0_QS@1S|KVWj*|dcU&6N*Yk|2(Z;|; zBU<#)c!{|S&Khd!^h?0>Yt@jo2rZbIHbyAR6KW~#!oLA&^;TMWVJ0Wl=Sgy+TssaL zvYp6@1^GJKZ{WpX!iXZP1|$E0DnUL`tGvZT=5o5pC+`X|=Pi7*dwa`dfj%EIv7bRI6gYF2ksOLL~r|Nk(!QkhO!zu>~^3sQ3@u^f>A3h60QLdC)J=_?7L{rBC z$(sFAQ7E|+3TOjOEg7|GBG7(ip1sR}TIqw@weKHA82crZeg-!PRv2j^OK(MvYGUj3 z!H>Cq?PB@fo{Dz|OwKt>{5e;=LQP-#=$Na{Q5Mw|CB(UDl<9B=rFFNAla!*;BG%0h${qmvK)-@PsBK3?6 zl|a8jXG|2EL*FnIn?~W4{x(aZ%oXuHD{PiBYR%e%F?Yz~+m|euaY2kYSe!1+qW-b? zg!X*+jn0iuCd5tn>C^%ZzmwmjHmhSGuR7h}W)r}VB`FhpV^ul3KEK*n8ey%w6H9V; zt(QL#tXCTdE;k>oDa6$_u(66C7N$?7s+9!=bfAJbfj0~KV%!c>B|=FHnDVu-{fX9URl#M+6}^2RjHbN+6JK6OBS%pu2lR* z-gJgYkG2GFwk`bpi-qXH0D6Z218lOf>WqEI1pjBY$LNiV_!7953iO^VLqk=4kosAkB{tKc71a= zq6H{Ms3o)FRkHg-Ea=aJKCDr}aH+)dgh6x#^j?|L9LQ9Hac5t19Ks7Dsx2~3Oh=r@ zN#W-pR3E;10eo=9v02`}u(P!2R98P^ z;1^M6Zez&dSAt>d3B_q|%4&_#3)yyA0T2`5ptWpI+Z8i{nT*vl$E>UKZVnEue~nmP zRe`pd)n(A*G8bf3SL-4cu=uA9sqYgJVr@B?WFOhU0x;)3=+5fAo+41PZe~YYRYv&Z zNnie*Aow*Tgk#X5G-(zbvp?sOa?t{u^{Cw;eqqkW!vIErEK2c@8Fz3JHPJ;2>)Gj} z0o+pfg_VFn2crn{qz+#tk(5i`nWNxH(N?Ve1~8caNE7%*^R=jXNVSE|zXFPUW%pq+ zfX?n>foSjO6+7C0LWZVYE0}8{0s*`&yh_o=irKXE_rM+Jv|u_Be{Z%K_1*M@#V7a* zSet)}AHA?&6P;zlA472;TTS(ozq;nWQj<~LFsS>$g6?`Oa;~8NACulcvRok404fTZ zny~lV1^;Wa(>dq{z8)|6PkHg(VRbd5e%1UjltINAe9Kyy^aN0y0LLOr|87%skKxUy z6*IEVJ-0A|BM zqaQxf_UeANj|qtM#c%*_b@}&E#WbX?N<0`W+1eQ*f0zHgvr!n+ly3Q6ptv!eWb@lE z-%{|0EskEfBJlMq(7+yZSlg2n{cCQz-SPxyKcXhAtN5{^%CWOykd55C_^Y&H5#`0? zGqbJs3Lp|NV6O(4fR}xD?fFfm$@juByO0yHZ@s4UqnUE}441xFR=;~@j!|82)ytb< zf6uTR{sC0G`jg5F-i{|%yXP8}l~?+f6OS~wliRJdZ)e{7;I#XS;fL}=#==JUQ9kfE z%zpdZ4>mjgi~u%c@G=0-p?7n8Z(`GRc|V4qu}5dH8e3I+ETPL>c}blyA)e}=C5OF> z=wuBBB>=_ebZ>*xg6VX&kd?D*AQd#ISS;hOsWxiveQwt#^$gK;6-1j!xS+divw5J< z)v140Cj;wTVdBuZ(yHV9#~Fd2qX~aRdL>j>X1FH1hgqc&9$9OF#XnQB+GX_N-8Tuk z3*UV)voDitg6t%AZG2~jsn@2bJo98Xqivvhlq3*Y-Pw^g!{#MR%EBLFNpCo6b~7cK z9U}YAtG(7;A~p+M_4kZ{%WCU$DZ`v2smm|?h2044I7U;kCvii*N~IUXO= zZyP~@wz|}E0FDnbkBF{+m3z`hempv*TNt1J{Oai)iip!!tgTsJRHS+q1~F3X1UGaWT0dEc7*%rI^a7IHVWQ@@WH?*4vCGs`eazJ@CbLfpqC^ zL;K(N;FqVidCJRF;iH9M%Gg2mDdcda!n;?1u2V48__{t8x=}?#VhCLcJnRwPO}_^9 zDMcJc8TaLj6nCrd%7=%Pr~UzX=3Hzoe!pg2?Zn``hqh=-S2DQ@Ki1wLwVqi6tTk<8 z*=USu7eoP=*ve(wL`km)ktwq2p4xYv=KwO(2geaK?f3^FYir$Td;SNmQ55mcgsyh% zgx1Xsu|xL`c62m!N7LHCs4^@y%BY2)qqq{uTgH;7pB<8_mp8E~pCf^ZTOv~$I#o$c zuEnHYz3Z)#b~BzRLRU|`G<}sl)zx8C4PLU308sB{uXo*e)ha6~lGfq=-dE^LSQD4w z%_0s?j#_w?L)o@h_#dsnmI_8ik9;8;@dzGj%+h%Tl}DC8V+o1Hr39o~p3rqKzB;UR ziBcl7{^|IjPbUvjZ1CWBCjC7l(y;X|n7nHAxXY&qTL()vDpJuph7K&aFO>H?q`pU4 zb@v+IPI>CJpiJ5HI4$VUX`u^*#9iU3BLSSUQ%$M2Fb42UTyN~)lKN_Io9rH5+$tC3 zt=V`CAOlT3FzB0<@#~WsEyNlEX>D6p_NJ=m{bWT}-iR*w1Hd)lico}_O75_QcruUC z2fs+O1%4%o@+9!P4oBH#Dj2mHS`)E1@$^rCDqEqL|C(Sbj&7qMj4<3OvFEkIVVp7m zKGT<9D<3!KP08QzXq{sdaftettJ(MPxO?LxrLTksE}b-Ms4QlK1BLo>)C!|5Pp+m2 zF2hgZ^C0!FJ0iML31=7~&j~xi$iGeO0zWh!-O|Em#+I>e^Oly~TQwZSXenc?5T>-> z2pl<3yek3)T8=qqvpg618=uHXX8)%0(Oir|z5LcIS5>8xLa%Bt!0||2D2%Wh#i>_! z_&T?jlT^6TRP}bfN^2L3kepw{xYv-0)dV{u+E*A}$`u_lc>#WN)->V)PefY9<~0sF zS{#sk^7mDm7z}Uc%>(*v>Rq=d}8>)lVG5@(C_fcN=K}I zYhhEgN(dCaRm)O|h@t-Bx+<6dy&(*~SaMvoHVdTF6hT^B0XrC%FYi%g_E|${bd^s+^fQ8a#HTY~xGbmm~t~E)}w^n3QqFg^AbF;tz2aqySwdRS!Zx|^ANseUdbs==kZThn7!QJ zJLT17UCNPYXOFQnX7sKIf5kI9M6dx(#o`4fXNyW`T7`6u6?BrGuZ!w2Pus zH5;Y&NHz2h$&cOa$+Y~StFa>X8by^WqlnP$r{wwG_Sl{i&f{b6{RpSR;@G2ZLo_@D zU8rm&VhPt@&PV!8rm}=U6Zecd39qd_oVsan+yN=j?@qx;t}?d|_K$5t`Ahm7JxFG* zW`otG21^KP8@>effSa~N-odT_FE;-LHFD__XEwG~IoD{X$*v_62t)N5*zu1WpZgfg z0I7X2KW>O>XW<1wXt_~?36Ko{HMC0q zI`OM+y0nRWy3;L4w%qj!5(3QnCJh4Dmac1tD^y+pSG>j)gnwxgc~sgHI3b;A{+L;& zU24FepzS#VhEjh21PAq_$OwQz*>P1yM^HxQCG8KL)LL|f&&yFQ2#eA!oSihBy((Eh zc|s)1P4rFmuwWKM(yxp*5P+M7QsIWc3k_RSHrLR^5i10>LDVJV*b4 zrj%p8U7~C{%*mX#TTKBFG9Abq%^Qccg~C(ZF4!&O)++PC;tn8yR6de?pnH1~GLo!H zz)wM9TU+zhoe|NzL9c=ux@`dYi4oS+Lzha$a6o-f;QC#YK<%6s(=I}`ZcQ0`PU=1V z4~G6Xb%Jlbz-$*+g@V8Prc40xkgxu#^!&7uS~drC%Q?M@f5>Rp#6xZY+WOTNkcxE_5tzVn3KYo#0S>#|BtHg zj%)IKzt*b7jf#k(5L*@FqX z0rymJ%Jm5yE_Nyqki;n+=_iEiz^A}5$yi6UI;RfYAZXPMgs?(75jQ|?8o|}LNQZh z{Joj)f1(}Ehdw!Vl}#MD4l^uKG0_B_S8%pehNaWggF<5{Rx2Pe&Zj+iLhZ0L1iQID z%7eYw12ER8zBc=J=2N(Yystz zG)8y=|Dys^QX9)V*oQ)|Ml)_8GJmocGUUk~2s6>CK|@pfn738huEK zjL4Pd8Chc8d@CXB3s-zh=xY`7VTMad8RGO6whA%NkXp@dt0-#F6ux}fV(-N4^K=bG zKM3*EeVlgM+qD(rhCNaNlMxY7q6c_@#Sl-@1Y2UXe1;Y!49 z(?-61?t^n+af4G>X?L^@o|%NwWO87TaO^-goJfe*PGqN60G=luG%4brsekJz;qJ~V zGd{^|-Q8zr8n(rDVeEo|kR;P_>Pt>GTzxVkJw=eF!3YjTW9Bn3IvKJs`H*wny~yA^ ze^i7jyt$t~BF&8@J2u!=@Q~Y!=DM-a>r#_?q-vj0sSMEBM8yRD5K7zzkQT(aEO-|9 zWv@Kyuu`whr0WSZ5L%)#Ob+gdzHi#lRZhimpUwE6!J7p&s*6$ScCeFpIAI|Y)VPmE7(V(@vRL*sP>Ed`=y3J^H!Y~Y3# zQ-pvhK*|ORI!$Azj1yFZ}uuFPcX01$$wab`t%_BWi#j#WyW>mYUnXvl-3@?3N z-yMK{Q&rCh?`Re12L82FIY#1B$8&aWhqvN1sy5iE((u5tM*CAuv2=63rSvZ-K)K(- zUaK*Cv2f3Vm4=XUWXSPTErFlz6w>09nhI;>GQrpb0e!GUSV4Il8DuMM6*Pq9n0_aVk(hI+K z5px=LZ~L%5II+>(q%}g^3jHzOh;V^e3R5!SV>lYD6x%!j0}y{-nF!qj`9L zPW@$^&Op@9LSiCx3-Q@Oa_W_;6zI+yT}79U=8nP-2Kj3y<4;KwD8H^Sxkk{EBL9H< zgkuFum%hRMZfW-T{UCqw2GJ0B`vLaq$e%oXB9k#OTi@6kzR&C%>Ivv`^PZOV^-XkE zhDOU?#mdLdKP((7s_1dU=&ANVC9^IBh6FLi@{e(A-q$phj!|BjGN#FYI1jlKQa~&| zn)WHXaa@H2W8w}Ltw>W=RwGY(QcN;&W7{`j-pMVk*ha003{v^EVm1qy+a<=_B$9?f zD88PSHWe@&Kg=FcmSRD`)r7phwPfB{6yR+n22WRosv_JIvVXJ%r#T7Ed>T6MTe0h~ zWa2DU1+Ee_P~Mj218&m2A_FEmh#2=aiq>`j)4hLSbPzo=gdQ}O&gOVcyksa>wAFT9 z`LdvYoYmizTTFW=Q*u?^a!R`jz%sEHltgI+`1T z5G{$P_!0>5&wMNU%8SPGAN6~1u)=-JT?c?Gz~3~<9-%apk}_nzj#9rV>i}|hmR10I zV9isb{E6Zs{07o9n7VMKZcvs4!B!DZz5vtqCtubPRr|&VQh-g?2HYc9O8*aUZ7vd8 z^F{^iqEwPmoxq$w_54{6y6N!Y_oNTNM_G~qRX}t$xIiI!ft6?z{3h6PjhmYrSc~9b zKvs5%p2bR{I{)fO$z2_Q0|DS`foU8@<^0L1V9pVxRXK+?fc3`^x{-Oyz}0x48dzg3 zk^?}edK%02+1SVz&;eoi%K{fu^xOme;~k9) z^Mzd`?j!Klsm9c({YGCe*cARyn)h5ZX+cMSHk6_Bod|iYfzE(+X}1TQ^$nUTl|$x# zji&%w_HU5PABJjvXA&gye@=ExxZK&JE3Bmh$>GY*yZi@~74+KtnVA_KI#0wdagR)u zg|h7o$2Ci?p5~(tvgJww5Iq2KYpn19=_}CLD2)!MrYmWbmhX4`zcOiUmD9E2zizjDkQZGs&RIes&&t;{neg}CR%3#0dJgCJ zw5%xl#`$I!W&WUX$d?5hND<1tolmiM+`Q#RB3YD16u2WuGC5wYtzZ2!fQzOSj zK4|YuuPy3y<&2B2WJ5Z2SbB=~I%hY3&vFiT7X(QO)MW3PBcmPi4#$OThRQS;l__+#QbgWwO*YA&G z+;TME=$+TWh?~9MZ(Zj_0eO59sYU#7c29d(lN_!k{1%m6eVIvwUp~#t0N;cJe1T#M z)a{TGxD2jY{x7@ayHY~t#C(Y9=?lhazS&XtfAVYK;cyvm{Qv4(eC5BOEDxZOfarsT za=xygj%anv*ABklp4kUlP8osq@Q_gvd}!iyzF29+-*@$0bufp^x4uv71IDzDYPs)q z*$=*SNwME}0uH7B{D``=SLQtHsSV~Ox}0h41&8cxY@UCF;K$Bu;dkEeX5T*Uu{ibr zG}PUg*gKLI^sXfiG;~2}Lp#3(vfRv>0%in{MV_V3o|#zST|y56!tMR1CMqraH`BVv z!uTs?@5`#1_q~gBnsHv#*aPPm{&Ol6u^0dVspEB%F9H1UxB)AOkK8l8;P2bWS`L8} zCiVz*)1x3?H)(9@_ScN)K03MVJuQ4i`f=iZU;R#M%Sc}2%2%IIC0sGx=HMCkaWeI> zliEL=$=OM{_VL{zpU~>L2hXYao`n0rt0<-AqN(33oEmp;Wi^m)YD*~A0*5v?nm}Tf zs~K#oC^>>IOx4eza!a?K_%zZ9UhPxf{AQbFP#&^huh(>7U=Dg!6t?*LSX_r zZ68h`Kfq1cceTe5XI|zUX;2(PWt2U+2yc__U%b~H<1Goqqv>5q)yD@&@s*&Ob@bI4 zs4Wtx8IlLkMUjkccPk}+EKv4bAScaEA{_iP&QE1@TZ^s1S@QNLwQJtg+7^B>R4V1c z!CfR9F5EGWK$C++k*F%X8Gq(M#0t!!jAw0$UFmHs0LRPH)&*-f@1xtO_tUHcYn^u3 znmk~P0%H>tAvfMeRVY7S8EQS29BZ?a6}X|D{OFQM%{k zv_yfD0}CgOZ^aj|C%$HGJP+TVo+=Nc20fPo&b-{bgAsKUwjSBBow12aC)@jz*JP#l zfz}e2rnG+hzFnZW&;3=gX8v4M8~vQwDTrVy;elmRY>6^ujzwRP zM+5fZ#OVb3N&1H^ZE1E}GC1QRw!e;5?9c=8MBpwT{c%iXx!L4i;vspkybJxbKdK8@ z71nhj9g_f+37`7)kJrN+!Lyg2 z%e_eR1OM>?X9@=A>~}9_ZbHEmedgwFreV0?Ui)VknoHAAPBzei!tZ0{A)ROE{$)@p&3WZ^IT1;VZ;jjX%&t){(M%wg6$|sWa7rIh|G>BEM zw;jA-wV70}g;;I13uobhaglBp8go`XmHOLtgmofVwX1{`aht5K6Lh5|p@thQBVEw9 za9YDU8g&G-6~h|gAFMZmfJAx?act}kkK2R%<~rIluZ+aEyo9b zYq*YGt~?V0vgE8P85+2Scwt`^6EXBmQ;$Tyy=L?O5E zCh;!kZPs>JW5NuhdwPtribUpn+Ru_q96ROW8K!&+zZXC%A5~A_T`(J60<;JB4yn)k zC)dm8=-AB@@MZz&%JhL4aHK77dM%Vj9Bw^IE%Kipt~ZxU?{k~BWK?hQCf0HF-_6N< zCF7E_l1sgZ@JpzYe?XZ13C#dBUYgmbM>**t!*ywf$uA}=Ttltm)v0CO1Z~j0(M(~f z^Lm1K$HAAV>)IQvBKj)+RC);-MsthumUdW%DfFe>LoF3aIjYMeJ_iO8ngDbe zo(lD#3S^hSB!SHR%|R$2h-D&q4PcD@5^t$yKF(TTN1hrTz%1#X>>`*+eFS}>07;*K zG^`9SKZd*_6+_lndjW^qqG*Do-?rYr8~fma|D@DMYb8qdBqy{jz&ED2hu4iO1UmAE z+LID!+k;X;N49dUmt?;Rgtceb-4LC&93)2SSLuWC*TeWdD;f}MEK#`#9i_35!m8e1 z4CeLN!`;uxgP^~%8k+*MTkdYrU+o;YUPG?R;J;ACGumk&MjxJ=N{b-Dlo@&CD{;r4 z{f>Y0wSuyemAgYnJ5<@uQ+eeb{sEdD<)T?+omQ4nHz@HO1tUZQ$ssA1fCPWOA9NnW z%8+v?o#EP7HNcVq>Ao}tWcpm0bJNgf zxFwEa^P%5U#3~`KKk;S3EvyMkdV)h1028|$*$N^3U8$5hoUs!6h-Fr&>B>0L&;hTe z{i5t>(=Q8J{y}%rx^&fm98D0wvtIb!{jz}iieQ9AF=VHN=d!Ag4(k}>tX+HDe1MVm zCbRn3=V}N_u48qkwE<#~i1CxVeAp13N9nL?dZ!#qJlNxjO;ei8p6SX`P-c=ypcLAf ze{Ac_KIok$-QY3QAzz(&0B=IgOI?3=3^7+CXBID;fe;h^3Dk&!-VQ@A7}1(q{p}I} z&3oVJ7}NS!QCwg4H1&3E1XFYXwdf)q^a1UH)w^oM?j$O z)GY0z2xzSd|4JO3G=aD~M+M;Q)LHA4LMytCr2+{sGjDK-r~|z-4X3cvo$RiQar2yq z)mB$eeB+PSEc#9V-d4mR5>qK`d)D_s&0Y;Lj`&dB5%#e%Pl0b}&Fkg&z4*=#SN{)m zeP%I4fwqHYyu;tF@9(}h=M7e^iZOuRQ@AaS|3&YPE1DQ4Q*h{<3788KaR0arT!p3w z^J@%iQftCvkZ`*1vm-(OeKe51{st2<%k^3SL_QkhJ38kA*d8_W_?8mM#dkwwWnvDaEEFdC5A@xsPM5oDMQlgr@ zHpw;`nC9X;zc=ZA|02a9w69UR&A$o2|0a5MwdFyWR!j@_`ZNQ?e}GcUGaw6Wp&so7 zS11!tuRu{}CVjqjd`20!Gyyo<&xbkaJTCvYO7Jb0m@vnGla%`o(?7e7drX=k<*Gft z)-5OG;lAYoj&qb0rfirj4TsBg2u%ta5pZ8xq}ov-`sjB6?bIna%=mP)|3?cT<0hx9 zB(?Jb$;zprtyI8wQiad#yfCNQ+4wOeJ5ww!ZmaqBz?v;D4T$dm{)?imJ+>>JIfEGQ zUFe~f&a7(Xf5v0*oY(5Y6L&iFRZqkAd4^hT}#A&=W7?g$Ir_!iJ!k(6@Q9bp>M z8?H&m{T=^h4# z=Py)+QDe0K<;WLe6{T!MEQ7Y-+Kd$2oxV{ zQ;b8@_s>6m2G*?h^M6q;QC5cTNjW%twy*s{bybn_4yq5_EPjZ=Tj2wEtMcD5H;W%w zLfQ8@Jrx(7m>syJ>!)+Km^4VjD}ivzJ09=AvF7|ZN3;(oDRT~CZ$Tvg=@?XTZhX$t zODO@mF9nxB9iT;hC{C%U=pm2<>Lht??l1U6SJzY6akkiSwYlT)Uiu;uxRX4~>i9E& zm13`EH`Un>36A_%%1XroN*W-4Ig3$C{E;ksu3Ry-Hn+Hw*);I~$u-ZC=02j-nL0ou*zX1?z7fAH_-lUmKGf{Kjv=SAC7f#CQoIVRyv`HJp%+u?GV4HlC~phT1Y5EtURY%G<0a93y{T>OS_{kNdNG zy+C#)57*h52}IG>D!HjOn)dH^(;~d#qwN>rPhAzA%u)DACIU&(y^rYQ{Hb^mtm1P$ zc9UMPFdSLwKC|yaem;H8V@)uD#-#dv=GQp@{YmA*V+tc#W3bGNT-%c$h9>Eohw`NW z3{wyEKY((|ssy=!`NYv_K5aBNZMIZoTuAFAVBZ~&GD?vWL6h`53dVaPL_|fp*~+2U zIx!I=pEbq_!&9_8CVdP!7tOE};kFA1@}=luF3|0*Vw7J*kJ+|-3zBZbcavnEr=HiHyb5} z)=HtN{i<{}fHV_!m+*3djtEpgR}>%wEqU;SLbyIFl`H>ku$g5&npU_@xcT4RlsJ+?eDhrpnPyM8t z>mDd+E1HIwR&{V%6o60foYJ)~h%jtSK@sJs4w4e@#CA*7>GlYqDi?in2|8c&4juz^ zxC-Q{I!iR*ymihRt;46OOjNrW;9|Os#XJP3a*Xrv6yhcPNr6_{d|4oF;?Z4MYaM!E z5vMk_%Pz?$O=00sksb|ol|>;NRifO3(Egyt{;d7{v z!*YV-#Mug$wB{#4wLMr*BKfY^jNA5Q!MXC{E?baOyM>``4(up*16nqe3jxJpH%Y6c zScO;O=Hm*r8|acybsk%q1#GJ5s#kEd)=xvRE&Y@r{hl^`*1m;E;;SamgJ~Q!9ZLwC>ti^L7tI`P|lhQNvuYwT?IrA5RSeb})%e~1O@#zQG z^FN*V5kO7QDN#XkvD*v4MQ|7uvjwV^94H!Ps@sQ7Iok&&3gMtExJLfiWdj*QVQ z1d=Lx(Ml@Ts6KA~JN>JMlKV)6qI}l}X;s3hYqk$B`V4&Wo)_p5XSaZ+YmgIzao}M) zBfVdMQ)4y6Kt=D%z5(A9DbWR*CPb)>+^6WLk!5^1*fle|IV$<^SMErYP}A#M($F&B z)Cj#%{~j`acRFb~{dKGw+-}C9#}#E72^_9m&-v>+#Q(>Z(V})r=wT?PG2msFR)ImC zt$7OY+fO~cV-jqi5*kPzX@J2YV|0kqR**o$q4uHgE6tO#`|m9bi3I(r!rY`oV|a5= zBY}f$OU5tZoWQ%WopJYc=GU`J@!cC(4H@C^oD%%l6!5s}axvTJdb+*yVQ)_Ft*Xo@G>NK))5wRN! zTrH?db#6<^<_J1f^yJmOZSkXs>;?2iR2j$Z{-uzUHauUZ94?aC^!Ey5Khnt5jX zja|R@cc?NEv+b>!Dcv2EN@S88Plcg272MqKX8I2Hr8ycg-xY+J@G)HgbI(HWjE#ha zD*uk;y9AGe2W?u3&_Y*>Xsfe?cTOC|C7^L=#-d_0SzIWajShEHDx$Kl8RQ-aisMeFR99E7@zao% zE`=B}dFZo_{MPs0nlXW}3cYPx8`0z;s9#BSQ&x3!dh>`)N)#95F~&5bmbRVKO>*j; zz_HI*7dA)suBxQnCENTCwiO#!nAZWXATZz^Ggisr+Jt;Q2Joi_jrL|!1<%)u<* zk0V!?dygyQAm;3$#%roq#n49xjw80wlGa~R z9$j^*-T4Izh<-{A*8C9?V|F|_g*{{Ro`49>@mXCErS%yHM1iqlqZv4|0m13HKwaZU z;~WI0ouj@IG#O~K7Z{z^p#ri@yeX~hB8T<4DJD!jbv}wIbJhU-S$rn+*=P2#T~!G) zXdqichxHK`5MFR+D=;K0W6W@*4t1wF{c>Oro<^F@P3pDo2$*Ijhr@FLXav`8)#gJj zN;Q8~%3!nl8>@A#z%M|3kRM;WNDx$q-+xE~*MW1Be7(Fmgh=l!1UlUlLU%0okbfRW zQW2e3TIkmWiB^KZQC(CR>@s_!XAVy?fxUn+`MktR+Ns1$^i8+HsCoq@M3#vm*E*tA}QiRw9<+*I_HxoTW z(asYFuZb+L0c!n35#Rn*z$5<+wVM^1qj^QZ(0kIIfEDeK5)~mb=hl<&OQPa1_r`>e zkw;hgBKRL{I5~5M!o01Z`--$OH zodHf>Dx4f0>E0QdakHW21&*e++gxgSK#&=$m3J_Rd}-Scizw+J~>@kmn1#!S-nL$WX0JHC0<>AkD% zYVOqJd9DFvTN!nKw~U}R9?=MG^!3~$O&cPv^~DPLA(6WZDtlZ8=D7yU=6EdUNp%0X?MjMw*@}_;j&{ z!S1&TpQH`u-56;eSvfb{5(5HJ2OD+`m%-df2Rt;&xD4^&0})v+0TzOY^PuwaWdXQi zxvfZb6TpN7H!a#mybP3%Fn%?|6tIK}sDvs_5}`yn*-8NA1F zm$84Y$CK0yUuvH$Y9nTeU5mUqN>o+;p;CriV;gS0xhpp5Nhtk-I*v&0EJ&E%o8S?t z-1|8@Iy5l5KaQMCRVq@dL^anFE%#9s7MMrlP#dRQEZ&;(=j*6#3t5@4hdGqpe#}4o zXT?BF%<8@G0g=Gc+3M#S`}^f*dEOgUJvzHG=4l5^YqCGh=7g3Ae9&oUt!S+>-Q4)s9n%B@M zTyj#y4yfaT1~$?m2i&K}nC`r^dndqi4R1Uu*>i>DU3_!a4!X_!=@hCaUQ7p~XbvBL z0z_f~M`q?TMGOp)FXU%unT}QNd1R-hU5Oy zwLM=^{8xEbWlz66NMuDZ9IXCOw8Uu2{Y=FTTzQ#o(LJ5C3D;laObvG7iBccY0@(9` z$=Z8&BaYv>g^%Pmcx80trwye<``YYlb?xl%&+CCaRzBYKyMOz&^ER2!0ybHv9Wn4n zMjKj0tsk>o2qpy`hqU;{HgL{rGURzxUSTq(fui$gGP)NLKXc#^NPN^OaI-@K7sMkh zBBV}ZfHUjoZv<%#I{@MV^$_>bOBTFbp+{AWIo<6=Qa;$;6>&u_YF z;Hh8P!Ty=zd9x7{X;f4IzzhBzhTp%&qnFNX1|wMPKu-3c>3wNa&ZP=A;badLcC>kx z16op3Ep%hmEn)OneI|5AL==LZp?*N+8^=*apkKQiE@ce00u2@Tj@nmJ%;a}&%;Cn+ z+349R-B7{&tc~ZP-xmHCqJNLgjkkiq1$>YBBE?tlzz_u*{krAbR2@CLO^g^9KAaKm z;5=qilZ`FM#VReOUv()OTv3zYGlyIL3RZkokG6XU0;#WD?82yTe(II01CR8uqqpvh zBE?gKW~QvN;{eNBhR9XZxaqOU$IA|W2mskVXcZyFeeaE6#wHygr=TFLU-KxnDZ~$% zSnW069-=48O&;DSo<=kN43;?`8mo-1i4hyp4H&b4Iq|L_hEZsNTWNl`ppSu;Nuk-HxI|6?kwjO7g#_aF}na z)GT-Lt5A+AKxF*WSGv!r^ahXyQdpr-$gq~tQQI7^xitzx4d}e*oy0LnW=e}$Wu-)k zY#Vjm#^jJO(lcHgWHo5YOIBRuX&5mhJGqac-V;Iv;Wv!GxHCmx>?+qwG9K$quVnxT zK3dyq-Z*6vnSW*`60l?M7meC&FOa#x%bJ&8Isq=kFdF4f_}W|Jm-+Nu>_16SpmX@$ z-4`!dc9uz-aF|gUfMyJVzv9!%87<193KRsiadwEBWLW!jSMF#@@{O@r|KF1V*gxXf zHzLywcr~K}To(^W-eDR@GSWdaG$+Ygr8mXCV1@h>YNInsw&exj#NypV8E<4*z=vZ1 z&(9N~kI`EL`YS|m7IKwkk*H+k%*j_ln@r9Plb=-ZKVO-;9DL^V@YG9$eWV=9-YK@S zo;Cnm?GK4g4UO@zkUQDq6lAxdMvpnN1AQsOL!-*WEZ2bZ%=`4^t)ji)+GC8u(I=V} z?9+6PY^b8tLtC~XpY07?_4C@T%yivOq4I_t%@swMp$ZSX7 znW7Wp!~?T!U9=<8XvAm4VIrbHx@f3oWtWEa8WAq&m229I{CDM}tUF3^8~qb#qXPRo z@UzjhQS~a}BeorLx)5%V*j6?B*nvn4D?@*%MuS&f$*~Iy0+y8QKVinC@lA{_R8Qfk z;z5Hbd%lyKGNP+3Fxpptx^Pr~NYW<3q74tCKTjj&m!u(SW5P8A{sy_TKRCyRRyw(whZ(_}5D#c%VtjOYTur$YiN)A>L()U&fk*pY zIRcEr4rii`rzqu-GpE{X*#KeC_=Gkh1F@Z;SkOhflFiY8nFl+&5MBx`V2LTXKM8>5 z`LA+U^z?1yffFZ2T|Y>ujv_@h@lGS$`e_My!)WZYdk&o^g=;;JQIBnJp#wX2o>pF; zm#wPA5}(%_jVHRa(~P8lSpI>saKw8JFzWlx7+R@sA+%c^6b5xe%w4?QVj|CQKOicQ z1j!KTLv0?dwbI4b6X_9fmdbfGQwI5NIF#`AW#NrX&+4@L?t%+bLp3p3@pXMaC1JY-OqWc3nNFxy-D)^Y>lmk*+Nn;qLST9Huz_IQoH z8A;^?4IK>`TJ2pg3p|*+^HTVY#3TN@Sq1@Rv=3dDFa(z8VrWcLK&f*DMPj9@r<-dw ze@5rJwAU_D>Spi|0n)I6n>TNgrfSh809!HD&8{g3lE%I=ho;NKA`eUAVGZ$1p}uxu zn5Rzw>Pd_dcV;PJIZi#6hYGd+hMtV#D_rzpEbaK2g z-sCCwAC&r{Hj?u2t?au@bqu-Nwy5HT6$IpR<@2{2*-#CUt=04c%6I<86sp91;0Ax31fZxf`w^`iwq`n#KVX$SW z+i%cH$f3nuyBy1EspUi?SRdVBQ8u&u!;Wt1@c>b`yjy2gb|PpnBq~qTZh4gnwQq?D zk;g<$3P4pgF(QdwY6|jJSsd>7U~*-Gd!)%mxFV&a{#bA5G0rWRv1R zMRDGtZCa!bz7dJu_R4Hq*=Q=M&nKm6lZiLp^P}ZE2t-!J-7t6gZ_AKcVn;9$m}vpE=-PQHYONAo$UL=o~?` z@==Pf>6;H?-w(JdGT~(VnYQ5Iw93v@-n0|+yo+~d@4jZmUggOOg*B)_T@gK20cd!c z5n{Zv>?hSfv%u(lr2c9=vGEtR1HHN8) zZW-i$LLEpkvxZGRzvmhkK2}_>v0~WzcNfKIABWzUpaU(ad)?qqwLeoeW);pv!m6wq zHMH=Oy;ba%;3QESVb55?vsae+b77j=;XQnI@O0&=^A|W+fenHs=g>(4%A4@R z<()Y~i?;wW&%0H-6C`b2H>MfNb&Dk9S=ED&lq;W`anKE`qFy5e>T$&jmDW1QW_o1D zy+)&Mk!iuD87rqWOv2GFr%7csoEJZ(6z(jmk>&bBO+8M&s(Y$}o75v(lS#Eqcxk!f zl;7UrQ$qCX0JP;0vmtw5H9Zf6%kgV&sg}VhP0}iOsX}o!(zSC8c)2qP>v2ci`~wp5 zA|2?_<-~;C?U=pfBphlmXccc@{G-rMH+ro@TBPvavBu1rjPXgb{V|kdSs?Zo@@Ofw z{Y>pYxCq-F_c~ky%e*3vn3@w)x+&p8SO3DPXPX%r;ZPN?c`#Ba2~=lo1+DJ2b?}l67))^_zX946jA^rv z`n}iNO&#O7kr|sgu9?fAL>hb85jv~bA(eh+MmO8ZwzG9_?fdf>totGNhZ96~tM+B@r zK$!w(=Hi#-Ydq}+Jj~&h_*+|Y9oNbpvr{~8yqDZSX_M-4`K`^`!QQI7X?HGtx|?iw z40|#;#%Q#X^;n7-W+*jGBp7Mm!#;*$lM)kj4bmt>u26Z|N%0hJ&vcbwn7&%?Zgbml z%b4x#1l-aWHh-TKs$cEd8-PgX4{m;>iJ2*gWY*JsYF|_`gLnYHO#$WIKQA0OM3Ub1 z${T#mY23@wRNWaM-fYKV-r;L7gy0y|*`nO3cJF}nYG?oR43RUZU_e*n4T!X^>8>Vr zaK`rDO(*W=dA#|ss-Y+?U?{UF;^wYCfE+=jCPdabjG0=oTA&)4JOMfl=Gfm#IZE_z z*_8KXm2Pf;@^LN1=>C1NWDb7tbX&#(AL6f2ot&Uwt;AqSaBuWU&D^%`~eFyxJuv_Tf@%aV^#(y-in5Tk06yv{gi?-ijRHe<)MgTuZ^uw#VZYTm28m@d-0s1$SKGZ#5S6XB) z9?*b!XWr`*&W)qv?JWqgLSx-e-+GqCifgC#&X)nFo^5_lBZDxR2+&mSL$RQ}e4f`r z_|ft^j?nrIUl>PiG1lq>=25lZMTx$Xh1?^rsqpNPFpwN zXq4TKA^&m1-;4)W`$X)v+m&@sb3}eHc@lqmfC9WyUP)uUSMlQ^ZxYerbMJ1hf1=x4 zSM(La=Ngl>Lbdw-wLe9g8q#wHZb;ri18IP1nphV_mV{SPuRt=nG(m8=e8FlfA?AAS z!i^J4={C|WO%3ihQg^nJmmx(J&kFR*eBFw`xL<*6|Aw#i@9=uwnq?lgwfAJ&^y9_M z(pzqs_A1oR+SUwnfYim*4iF+He?zNwo8GTWaMST1k|3fvV{`86L z<;!SgLQ^+!KaR(LbRsotbviIFTBrz~+|tXTEf!Zp41PWS!s`3nhq!}?VrzTk3A9wt znwP(o#M5R>0a8!)QHHLRS3*aG4Bqsb2C*ALfS!f4+G7rH;- zLJzyh=G{-TqlKY+IsQ+IpBW~%hI`yX#lXuD3LlOT=^_moG`MMbIYryL!OjcQUNRNN zkJnwl*h}>Yg?vNMAv7S$5lOZCxQIpSZ}@9*XyL)jXTyl=B4gTy<{Xjh*-0PCS42ttUtyubNn*Z@da{Hzp?)=fCuS=Et4tu{(m^ z?Nkx1_x>Z~)0Ob@)ad;S8}tu&nH5KDJTa&Dk=5^Pn+BcFu<6kgha;pveaeZ^$|!`H z8zfzNS(R#U*DV-0>*FrHxn}_0IAOoU&leDQ4j27|1H)IYUmn4Nj}dOYR964iPr8OX zfI;k29`xCHCsZ4|WXF(~*7k6cd7}C4o1*GkYq6DvBv7#t2@TCYZB}wbEDR z;{2c1srt}wJy!_?!=##>kt7rdM{kk*hCf=}y?-i(HYKo?Awz)A*DqmQYCql+t1vS) z__4e>Skq^^z;=m)M=FJ7(-Y-Zk$&er2uVVvI%Kc+swcIEJ#YLcX{)Nda;&rwOce5d z8oca?Vt1X<9W+l|kvLRS(-qx-9O1e5`*seF873 z-r2w%>G7XlN*lOKD(%H2)S_{r?e^#~4M3|2u?J@(72Xp0ob*sH$=q3e7$evfQ|=?aP8Oiz282 zJqQx-pGonds1SHZkSr5U5s$?yeuUmwS*_jLQmf}Gts0KF@nY39=z!hmSF79j)bw?$ zfwTXjb*@8c-_5%sFy8Y)X`l(p|j@dN?GQ4gpq<4cdgkL1t zL=|8Y%18^~X5AGf=1xgQR=xe3J@z}k=uitOdPGiGD35%peh7Q(oTueE*;O+$?)%W# zv`UigFO5jD;|%)oKH8)B*xSh`1}b+Rqq8L`QN_g>_MOh`0PVJ($Fj?+N3}a~zUhEE z+6HS%hsFoP0pn4j%dG1`$3j>D)0hnL-_ooCYxjVI?#L6xk;F8f^U%{!uDchncC$1n zFVylPY4u&8;^-=p#xzcwzCPnF+rDwK;z>t#XSM4rv&ok92qZ$kt1o_bIyu|JrYzZW z)Hf~~f{TitS#qc#HMo)O9Fyy);sjhi*V{1A8SqGkhIf)zJ ztXfyC^U>7b?3(V%oaNb#HkzoS^NJ0RpLCTvw|#)8rc>|K5Lp2ShwZv7cMPg;W-%@r zlXRpZw!(u%NwtoRlI!|YS9B;RoAk6$g*nOGHKIUqUX_!`^1l`V*qS3%`75IHpgNbS zN1MoC<*fs_jqoG2EchO|sP!DF;$0C9wT$jM73E=5# zE1Dr&W^vI#Z4I>oz)n@oo9!FX{br!*=@ecwL{axmPRMIF=9l(@%%-~!UwX7*PUm`~ zc{F4r=vsSm$WK~njvjY)6R6Y+>LTs3`CC9(0T%WBTJBNV!Jrt4v3M2s*Y?^^yJkJK zCp<#S%Q_l!Sv0gKfV5lM$>mJLFdZj}goR<2RiP{TJv=?U>`HW%&k<=Q!xSe(!lle` zJ>#b(#;Qz5G3jk;rC%BK2s>xy6lIaH*aOsI&J17rc-A2CV&Z|FPPbm=2Ix?<0zDIP z469by3}@cKFTQ{-FTN^a&zrFI8Z# zLlu@Lxg40rN^jjruP~2xet)JqSOADjT8KjCoRN1kQop*2U7|X08^6@jsqyH#RBMOS zM>nfW*29~&DSA31Dp)}|b87MEfpk(?UhdZH0Ox`6?s#QQPbAl+9bXlB0gtzCtH}nm zu4O`nr^5QqFyd;36yRHobvRp?wo2jL0K^2|kG?E;YM*>#&cVqIX&3-;5V@y8osg>0 z*p{CdVH@PL!n)eIx+pN!d@1OpLzl09gu8ATB-q)*h8yQW@}6JSvC;}DT{cGj2^__n z;1()LC=|vFRNPm?h>a4o$;@e!YwDxyEt32%3$Clu49KNvjYtq=z-jQpgciHs7w;4re9a!u2)G8k4! zZb=VctSJICUMDFTV{h&ibaTb(`-;b8*XGFaD*)Obaae+w9x8iLLG9@aXaTb_l--)G z!-V_YIn)mVW)(G8%=;|$lH`If9s@HGrK8!2M%T+WhVd6hK5o=qA~;trv>hy;&d=x6 zf(?4Li|%R=$8zZkP4xOTqh4E2Z*m+;&=XWit=N~MnpRZi!;h5CLq_o=aMipasYf0j zoQn@NT0K$eal5s8`yh84D>vu0g;tuWXLZ$vq?ya_LtfsOEG25!pq)ASLG52`087mJk7#V+_J9p6O(Yf1MW;^?v{m8^*ZKp!4_TjY=hGho~8NaQzBYDwV^64r(#4g__E2Cl^A2C4 zQjZA$T^Clyl7R-W0km2vH^KGrmOVlLmaNvkJXKBf!Lzr3vD%ZYqoX`)gRR5FW4&f0lD1ZmD5tQOZ&8cCQ>rZHNKXx{M*DQMn%>)wvVg2q!^vTur%arEQYD zYmp`qTf@*>JYW=bY>82hJx!YY+30bB-vs@H-|Xz7 zl^)kg$@Wxftq10MR+ZYt(T+u<6NfwfTD}die|V4eyf1dH)yNXgf>SnIJ{HXEN;>t( z`{mZ8-m}efKE3mBc-a|Nu+YwavPfzdCQD@009qw>jMl%klg0bqUekS-|8#@e9U$>E{U8YG4TCK|>iGti(E4Edj5hO6y!gWYM; zj|*o$SdaY>=MtEAJ6u)8e+=%9F|6r8Tu>{O)u5H$??|1mW$io!SUvF^GW@W(1(3T=tm|nIM$>eVEJQ?^E-bs-J1pxd*Sra!fmvUz52M-C~==%@OuPaCGn zxT$%fVSiP+>4QCGKHGFD?9zB=%e3{Z?URKE4jat)NV+B#aIvu;Hv)j{_u|?XUa+@{ zFkWZo7-CR10d(N)-SVq@IS3I$l%)J{JuBwmq0WemEcuu!_{`Fl6M(GAY>7?0e1st_ zD%oRBAa?5h12lD@Ekd12W;)C%*1r+=@9X&jG^uc6{VqdbUPO|r#=94MUUH7UEAvUA z5#J0J%cpJsL)CI`hG&^C`3Kr_`A6mU^wWPJ)qfUU>hi%tjBkRfhjRPrtMl=`)l~98;X6 zOtQ1sHG^=lDm%S$n-fHRFm;Wt*o5{cO%`kRO+OZ-ldmTJ(kcQ( z(F*5cG#IWxu7X+FT5xX@>R(UIv?C;9JJ$4!-}ABG`_<<~UL^0*)}=qY@eZ9{thhdE zi2mumi^~5~m>8pPW76(oZtGAQ*!C7g4!T%vq&nTM-e%T3GjS#4gVN#&tLL4mi#zA2 zkwd|;^GxwcV${4NzBjJ_+oIR)Q@0=N0(@zjY1-Oj`SaI^2Kr#an{(a`SQTtMm;kz7 zmw<}mC9YIIYfHv_`GK$yoXv2puQuaMBY8zrQ}Tn}E>Th9X4W6&gsO9_&6`R~Nh0H% z{Lb8q$%$@W*=Z#SFa>7Vfv`zjbS}Nh4@TMD~hpsf74V;}?CO?;yq!9??CfL?9yMh)OcPFaKmvoJ_FemRiA8;b$J-WHC}7I{;-{nPSIibq<5xlhX-;>A zZn}q%yFYXk0LY3@8kL!vlperZUv&(AlW8hj3oX$ssoIkHzaNcfdnkAbA7^aQu>5qk zYr%srRoPE*h+q&Doq${iMN~52PEV9S-0_6j&K?`oBof#O5 z-!|~Na8P7YUwiM~`3;LpJ&V;oIMqP~K_c>?ok4@5_y%p=OPA3uv(@c6yQhX0CrE#N zG=0Io_=HiH z+gQ_U+uI(MwzcSZp%%qJ>w}zd3Yn7x9)V=sn7Qb~8c%TXj%s#;#l?EB8`@sMf~(Z} zjcnh{v+#hYTqbfq;dY}%s^OTton^vY&vf`S?Zau^qr?At*!x3T{J`M)-a@lqJM3MI zwrG%i%FA@-lT9RcBhw??-9BuQ)OlfIp%*MMJ=>9+yWbqYksJi|1cp4H%EKOKN1N0@ z-FV41j0bM=2+Sa1WA5-Hk4sQUcBLT{YG>uy_^WVbOd+V&58tCzzSN=+!d-fO3hza6 zw2|7Qz83mByD*LnlB0mwWT-E#*Wa=LSb!Ge({sjT*y$#aa9)FATC?4k)4MwR{FRD8 z6)wE)qK>0ckL^!NbIN=$J)9cASTC+0ov8YSeUEk;Zun)Bc zGt~4{?sIPg_u@D1G-IBeen6o$x58A#O&bmbvB#wvJ->Tb?3qT1FaqZ0pA<&SAW|N zk@PFtg|pIwdk&!e>05eK?iS@`(&~QF{gFxn;v_tfT3ne>-#da;5TKR=xm!*nl##Hs zdHluKO1$Di$e$MyjRs<6U1kb>?d_DPSayLBtPAFzHV$PX#hr57G~Zgum`#jg$^AhA z4{(S<&zh*Q%C$%`br-#P;VH;L?*byCgU*fHXY3BRupQ)qzs$Yhq%*g&>t=(2&e8h* zdb3;yy;84F_`5iT15cGz1lfR;Txp=;o->r70eGbf`&CK=| z$E?a{uAy4vakTG?e-byKTXqIZVj_gUeZ4RVw`%lMZbQGG$(alwsh>q+1~Yo+$jzRo zSy{Ed2CIML$~ODPx*2aYRW?j1BgdS3zc=qO4C@dM=-mQk4hn{!1{Raf{O_&obMM(LR&hKFnizwuSLtq2l4 zf<0NY3?#qQ0L<$aeG78oUdP`_yD*oK;3~>x$|T%~#%mWO7kUE9!j7y2$8wds?XU*~ zR1pL4W8Yk=c@_Z95Mw!QTca*L`coi^kXd11UF} zFYN5p8@!l1=a}y(@-gW~c3X)3t|M$SfyFeT>FR9eiQ}BOgos_Kor$j)g!0^?f}B!E zOT2)6dzPgc4ZZy!$_>z9Dgf4QX_=>R0)t}BDfH-50Ol{>Y+?fNAB_!xOb-=#W|w`9 z$9Z7j=<3)veVPkfng>Fj)yHnTYT2DXX|r<=abk_*@oRkIwtyMvu*Ev%VcIcqUj$QK z1{jZM2{=WgO@FDxs%0!{gsc>4^*kr*+X+ujgxOnn?yBq}7&A~kFDiac4RaV&k@Rk{ zjlAW~W&YC1kTXwMw+AognUz_QJa@nJ=FOTdXOo$|=bLU0POs^69gj~Sp*H@Sy2siJ zjUd~R9{~%i2S2-qT)NE^+mt1{lmFLiCbl8s4qUK18>{j5R^_CQlYYa`ZW?3MjM;hQ zFKLr?f>l9r;WLh8hHtJkbl0tE^l9E%z_&HX4t1t4S&enz3$Hxi%~*-wKs!2gDWURl z>c#eBn;6~3CKCw$M8x_Y*c;ayQ-3^sq>isc=mz?iQMc{4_sqbT@BOxtK0WWTQEug~ zu65fZ@gjBW;HqzPr?NY)N(1e@lJ3n+mWKc0lpl{1$k&xtggCl|u*MtmXnR$K^zSaY zWQ3#mK~18qME`#7gub@8;f|=#_1hPTeFUKKF;Y7_={wIWK1?0@G%d21tmO+0Zk204 zYyRQ2n+Z;fN^w-fZX}rG=OS91!R>cM2=`&MaMu-$<*`G~Nj9;mLk~Wco#oo?E()aB zna)^yDoXyS8QfDSE;xt3lED1V@p$#|jPvnzjp>aB=NS>(@@GWhw-qGdL%S6Y-lDsPXQYij?TR7WcU#~)(s zMh#ETC9T0?+QBYSt`R&qHI1#Q@I0+l2eaS&PvyYW4Q}dW${RN3?z(s?8k{A=wWoOw zOVdRDaeUv#h!*i{wj8!knD2+TQO2uejp!3PekjBP6vYnH1cRAea2FN>COfS7sImQx z1ym@rO&F_y^cYX82c?fKeb?Gp4i%B02OnlWFY0yWB+9&zy#1hhKnZCGu09DGT`Dnu z1I42eX8x>fXdt?@0W1Lz_>Rm9M*o1ln@`VAGUrxutu`X+z}*1KV9#FMwDH1#J|%93 zd`oEM72+y%ap{kcjI0^-b*M}T-tx)Y!@~4b{KY|XmMCmi8I6mP`87*)PqsZxRDCz0 zGQ}WEHUw}58h=${uAKo>73-@?@s1)O(k?h(T~)7QbDqQI;)H?HYW z{)m=fftvWZTtP(>BZWX;e`Jc(jl)*h3P?P5J*15u^bl**!W!Usj&R3`*>|(&+?<5T z(S!E6px7ZFIkbTNKA;HjlQ@ceGp^W1nW}qmaxO9Y+VUwlA8V8m5bG-}NO%UA`;4_! znwr1cIow@%Y;UO}PvY3_!<6e|_%#-AogsyrH=?LZF3}9HoA37L=X+w7M&_^$I*kbS z??tV-Q|f4W^u8i0R{LQe2pWyV?kKF9ce#6S%q1Q5F>gM-{<`&%9^tx&}{0U-Rb` z_oWEyPlhn0$flOE$N3N648}kiWAYI0+^d(F++rf4E)TtZ0|QY8oVT}E0Rehj-rV=> z(1uyCHbDiFL-|KZL(wEkL&ECdzqQ@}2WcU04W)ZJfj=%r{Qm?X;iHqA?~CNO@`GCe znK^$r0X7{&z(=L4vddIsvQOYK&FljyHdxT9e}y&8cR*#Rrof7%mfE&XQ*+Z`C<{0| zU{4)F68`%Ds9e%3fsKxIX2Je-LAY-Fj}F1OsHu_l_WyW`{vVl$|C5$gAAI;3Y5SrP7SMX+x9eqo|5MF%kjxBDhIuc`d&cJ8AY;wx zKZ4D*-viy5nPw$hcv9$dCt4ZjAuF2T0riP~e{9+iHh}_1C0LP9>hx`2k&_-Z168d& z1~$CZu!@f=93n*r?Sh7`5LZ=zi5TVVGAJncO2QD0!m(7{QWeC z^!WKd!Pkw&~LU zdq^CpyYq}K4}x2tW{~03);*j3Lj1{-0lw-x`C-uK6aBo z!i&rs^f5h>-&zhqF~CYx3H4$NXC7iVy&u*LW7nxE)i47yJh~M>^8+;scoOPEL%v$G zkFRLx-K$j+&w2G2&Sd%cWCx!~{wBozSu(Ft?q8A;{FIteLtiVVDYCU8al}VZD0!hk z*URjId8Aog@jPmepEq>8#Z3;9!S}&A$oCL|pvNB?FAaCI$?w_%rwZU9JdcAfJbo=b z7tHaL@SA;8peUXkRzmk>Nmdh>6~+w)&lzkItIqo)BrjR9cGnMQ&jQ-92G2Y%h=C+H z2IqWTp6uUupyXP`S^GH7+xeL%J=y2K8!_Bo@F|e5Anlt5&_K}5H}O{5T_eh$fwZKc zN=Ez;h?We>#n=zr0@g+~kdDxg#@+rXth>3)^0d=va#WrY zj(qGMh_B+GXmP&|PhOG%jpJ<*hzGGo0Agm>zZ=_|_TkzeRg_TX3 zRa(Bwk7p6gDMJN;uA~y_pV=YIn)Fg<*{rB!;UomrB@n?7LHd=IK4PdmG}_CL3fQpR zdqC6an1p$w+$T4$uh5^kl#J-GO2ySAdbYM~iD_$i2 z(EAZ?;-K;c_u24ZcF#EXRYf^aiA_7c+zbNSGZ(yR-h*4R6ij(cBkCFIhp>k- zR?oEkhQK6fXsN_hpiNOmOMp<5Rr+l{vstM*iNBcKB}Ek;F@~I`v7MCuWQr`S3p&>+=-38g!G zusKMH1Me6-l7OxU7xoP~x~e1K?f&D~a+D-i4==QH{UyH>Jrp2>BJ{Ul)$NBC7oHo4 zR;$KVOSaFQ-g-b1*Zg`#-P+T1vC@BN?`zdxFu zs?g7DgpzVtpXjf6+cIJ-zJH1y7B@uhr|45{YfDcgW+6fDA9ly22rB&S&Q65;J6L3W zuY2{!Z|-?Sjsy^v$WP1lj{$R;X_D1nT37$9w5C22HH8{x_^^$;c@yq!ZFUvwz@l=+ zGvLB_bUo>WDxuPx3i!xrN~=DqVz34;j_!AxkfHz-y0xdUeK1S|kTyWs=w&V7M#i3E z7Y9x`{$6%=a$uQ~C+Gr$Lk(>KyM}e=Dm9Qxe2qkiL6r77=&z-`akazXTMSku;9_Qt zHj=Nr0N(|@J-aXB=(*NMKh%Y}d&Jo;zR_gpHt{SrC(DIAOeO=V4-wKlrSh4$z#_%< zlOCE=5GTAxVKMV9P+jn#!6}e2@6R$tR0D|{>6;N@y#_uXuES@-WAO}WLm`aDJd?@Z<}PdO#+7q_9qPtZe-c79-b@bKCblYximHCV+6mc*KAmpns{n5 zDQ*68kQl+)B#>q&Ldkca!FjNZnONatnyJSw_PM+pJca*42fNSt+=~tcw;;bTM+Nd~ zxwZDA4|u7dV3{~_m{Vo4FDx~9(g))J)P)Vm2>b9HxMH&JFmu-K7$$2jV?OjOLz8}qYP zki;utZ%Jv(YrYdcW4o>10GkESb>n%P8(@mftJFuG`7SQWbRdj$Z4|)anTm-Ye zczjF6`%ShQ38S$uh#4yL`;YvGY)?3roNkXN<@1KZL<3Mnkx5`L%*%$`cEiJ5AWR*@ ztw5Z9RsJeIQ|0&4Tah7ii|?MAob@qPYG6k|t-zk!$88b}>R&8zs`H8WXsvCW4+#cK z~jpdW$LQI%oVxH)i(F>-iaAKwiL1AzCa;cW??v7dFT*o7V| zi4gLurf)vKJ!^yySH&MNAZ^z z|BARjACwL2RgpZ=Cel4CZB=gD2e=^ubqrAlH_tjwbqRcnzdC@psZMD<=JI@g8rw|g z!$qbwy1&T89CZJxGM3k$16M692XCmKSVkT^5Vbqwbfv3HKT3^Tb2}=&XNH(O)rZeN zTKCC~A<+2YeMvMY==87(v-4>-NC zGdwCOZcy#6b|q}4W|4|w0Q>gyqGiU>-plser$Q)$zxt#eUmS_#5N}#+b$Tn|+_3m< zdJs}?KpSW$RnH(Fuw*F>*D~&5+fYAM3QEd#PZSxxQCN263w1RQ3_GNWovNo7TLUr@ zH=x$+{%7HrO1p$?6Ece6%VV0t3PT~WhPf7}i1vyACDE;4*K~Y!VAO9+wFHnU zp@G-fwhr1U0rLjFcz2mc=z!?-$Wd|Eb>%TgR*i70&9XETHuH50VK91|)_gZsNr~K( z4R8D=nS}gO?l%ZU`7*VRxLhpRu3=Ht6nCmTY@#lwE$nxZPr&iqr0iWr+ibP9BhWy@@&Su0u3t-o>%F|FBc(RVh$u`;H~ z4iYeek?E0U;QjZrCkQbIhzqDfVmhVerB|{%Wx?6>X7Y=$d=dMS5|6^;2_tAkKsHy( zS=AfM8wKWcD!RH%KLDEm)e!zZBtA(%w%smK(&--(OzSm+1}me)@zsFQX()gfxiNs^ zS70L9CVQDmgE$)iHd>hHj3-~`&=+Q&!8?ON5zaVw!6|& z&y$^}Xy&HwCkV_wl}U>0d~V*;OJ3{PaLm4at~~x8*%r5AC(iaL-@T}G-XX3j-aTO= zdr*{-xJd=1(d0h*;q0cFU|5W0?Cy7#6L)VP>Ys0X>XqJ$jh zD5z(;s+5A-%=Kw2L-9OVT!zC^M1U+h^ax+0@dIRXbxag4wY1kTg zMbJ)5Ly3jES#a*viEXJd_oI7lR)czr>{UNQ>0~;DS3AR1b5c3MaErN8Up7-c_?Eky z&Itgm1`OWscob#~lm3JXR+4&{+7@mXc-Z$*98Oip*Mq#7ftY=yHj?#0YwgFMnrxkO zxnsaEN37nSwfhm(_4^!3QDKoHhtTQmQT0pr!wisQFaxsJBX?hHSq0%Fia^%*$76~9 zyx~y%@GWm;s}%TnB^C7O{38R&4sBa&CNh{A{2|YO@B@HL078o#OUlZg4P!C~ zoK)`@41{N2G(N7Jrp!kmCj-un$zc+A_|wV9+AU)rLlhnBc1jvD=AO=eaXj9p1F(<3 zy++c$_}#SxTr+-E{1Lc4{~eeJHWdVb3x8d~%Q0|viHBiGk4?lXE;bqjPIQOR&xWZB zy)55@{7A|;{|0>B`irT`sRg%c?!mFx@!Eel8fOLnkR+hbU1)8*Dm2WIxvQVp1BglZ zA$n4l;w4;4h#!S-IHZ2e@hn3L07dzarjh5?VCeXvoF5q5@MsYh#*U=~^o5O%ur{={ zs`^1e)ebV(0n8LIKcBVUEr=9yP_&yfb@7%S_~y^gq!;qU2a^-WCQ>Y*g*1vW+BA?o zg8uUnpoWBL&xs?FK4JR&kqm+3j)N6n?4uLklR&Y=RE{(Dz-8beAepP6zr|@^Pk%82 zZ2do4uMZah@~mm}u`TkKMb^W}Dt`P}Wa5{1bA>x`sC4t*o9sF4Xr%uG&xdWqOYmdn-?5eI7W8c@{}o^0C~$HC zy;`Z}L4f)IcaH@l#?OlmwZ%=HeP;$<(*bY>iodGRDxI^&6Dr=3G2u@pQva%r6ZMv6 z{tY7{(o_7(i^)Im&VZ#`*Er&EEZMH-c4I+t#+{`5JVyicf8Za zTnWKJ?&S6q)#pXZ19|o})W*kck@4FNBDOPq_{|Q%HP36%d^r7b*$*L(!)5VprfgR zZkhSC7PeV<3+$40k>SClU8}$A3YgX%-Fu9VmuDY{w*vEH+^Ux1bCZmb>B+ut0hJX!e~xhq%*tX^yNop&jgnuZyyHH|(eo_(2XqrJ2} zwAB2EU82{_(UokCKbpjmXa9FAC%3Vvt(209vs_F{+FavyPi?blf>YCyz0^+m# znwFYuxKOIzxC~eQf*w>}9W{ZL)3?Lb2L<@v#A?OK^3v9VCTNnqloljTzE^oMCl}dr zxV}$asC|3Ox8-DUpdPpf9?@dF=&53ubBZvI z(i2#wc?TmP)*SP++fizJMikaMAAEk>W|DD#+!O<^`pe3IM6*Ols;mn@8MTRYdk)H* zMM03{^QCj*d;z@>A%nmd%RQurSKWVpg$^1w%Y!IC6|+h489tZc z!$@>e7+lk`sEK#BJA8T~+Xrp(FV)@j=);i(Ej|GnWBMpbs;UJSflT%;Xdsg*KD9x0 zovuYC+lcp#nz2Nhk{6d8=}U^)*t-t$K>MDB<{)V_S-Yyf%*7Cli}FXyuE2y(*Ze8GbY=vR@CG}Ct)MMiNXl54 zT{HB2TD1 zcX7i_)OMrVJ-Ax`P2l3Iu?qJsKRk~cOOLY)baJG-&?8#TiKlYsUttMdz5YeK5at7B z5guJPEsIm#4zN2|xtmI{D-%bzWOuo*Lh8ix4jt%GaYJ1~?k(HxQK@7iF5JR4g?f|T zMk5IM>WF!L&ZgpfU!UnYLkC8vo1uXWMR9$10j0-4#*~C%c@ke%B)SgZFn7*xihC&` z{Ibd94_I&p;hx$0mc3g2_N)W(DaR5gSD%*Bo|AXe_$=v;;fUkoS5y3gQEs)ZB;9Cj>r zHG3U3Jh05onD=?nHY=98>FHB!=Oa>g+XZ{5$pk0od=66|EmX+Hm9Je(Dg0+y-PFa$4w$}= z_wffg3K5^4SgX6P$~4-<{+JZU< zDltHJ)gh0L-W=`U_wdNkR$>ik$K7$gD{4n9nDM&At@P8>huEs+$aIh&#VSInPp{Y4 zEmGpUDfOIC0bfHqRDlk#aA31u>vWIi>>`VgYFOp zZ5syB4LiEwsv3O3Zl>`ofI1Seckw2&_gSG{Y;jvJppZT+@F61Ji&eMaQf5=dR$`3xJ|b}cXVNDE@z!@u-ELztr>?mSZx9v&AnMTk!Tq{qF)Dz6)S(# zk(b*%Krha4-eF%mX%1?!Ce2I&okDvI$1a4?u_Uvz;dk?$!kpqq>0?wk)KbXDd0QEa zodEu*gIK#3m^DPu>;-NlYl-&lx#k0#wORvm7U5P-46V8G^;-*%7_dR|*r7_5lM0WO zgPKdfrd><^X)km8-Zq9&QZ|=RGw>-mM^bBy$e zsaJ3Z+!QyS(OUxfJ*XSKj=7f6Q|3M>JUf6xRvTdQ+(`pvkt4RS-cV^5rJMC)%3 zw-s%i$?ETlER6rDG_V^%OKh^4Ci5Wyd`!I$j8CjNowF5(8)_c%c$e_tG+~snMNIip zhR;{*Ct#T#;2e%{Z-qS=_(RWmW5swLUf+TsBPzmyXbSy~TR;G{Cx9#NyBjRI)?R;X zTxF!9#ixvV{;En|Ymv*3KfyT+A*Kn|MPi1CoiSSUKfMrP`+&Vs-309EeaYWVS2!a3 zRt-DmU=)41=)=D2qf231CK-7=OtqpKyMYKaTw!Yix_y%!DCurJtnHP(R7E|7YvYe_ zp`kMXtBUYV!5l_>1H1%C#NPDM=adHpr)L~rxo*3VGIri?!_o@wH6xXHdz5x1Z~o*g5m{Bp!kt;S)Ci~d@RkZNpmX}?5Vi{0z>v+kt_MBt`Y8av2%a!x5r=;)#J>2^ebwFxaUJDas#Y=(t4y zlqx3S6`@F~mL`cHKx04n`0bwPTUt>?RK*9x#RmgNzNV`$)O95q*_a^8b zdN6SGh#}c*cg2l+*^5uFI#dO!(-R2TJj+np5;4Dlu#t95GE(bCX<)0vf<2uC`Wo7@ zLNiW8fcXL*>GS=E{lrMp`f^CPN9g8RWRL(vfo3 z{<`0;8h>lpVs9H;0&DV}$mC`{QyW>xv-|wJ=`RPr8GPj(?9v!0CHX3=(nsCZ#q^s` zsjfB2daX1qLA1IxRww4)Lca%B-Pa?x3-kz}fBK)cOg}>Kd68Tj97o$hkrwA3!02I% zrlg=(rBhvxUn)7XIB$` zTo}d9(n6c>+%qnwK!GNr&}2I44!6;&>y@&=3}>cjAz8t*6a{FpE$CW|tWj)LN1+H` zZpA#G=T`Mj=_V`eVlulFbhav9m3PT#Kg~TFcLwWg#mTsC>iXY~4D%Smw{LV>e>R(!$r=_A zrSBfdX=SjJ<=J2NN9Iq2%@;Uy%GzVEO_1HuSEr(x|AYv@U;gH5^eG;HaUMPL1ukG1 zk1XIh`1t)X*~U-}@H3%B{)-U-|C%q|{)x`w1*!~Suf)y*#SZ)W!;;i##VYs^@El72pQ``^%mF zCCK2c|M8~r_zzt={15XY*Dvj#&uKljK;qR{?dg`Ku+6C1UmfTIm49DGb7uaoVT;<-J6X?9ZRX22xio(1c1_rZ2K%F@w)sv^egUrUnmWC%%aX>lVK=YrS!Q(WjwTODFrlo;&uapd)F$~nXX`haO&{8t+_mK5N0B@1PJ zkXxM4X|LPbS?ZI{+}6F|!?Q!W5J_937Yf^SNH-gY$isR!{p-L3o+ubmF{LW=lYTox zusD$_aF^XbfZ@yP!`6o|c+=850D)~1k4Aa7gO+JUkc5#j+^o)&JqP)Jx_fLHBN%)F zTI0BrYh>A1K>-r9ysF6&gVfg!fY;Ox4g^mgIymMa`WAg#ZxeDYV}30$2EwTlQOJg6 z0{LPv=LmN6TId;mu8NPEj&3`gF*A#PxQR+413xy-iY9egW0AA+^^=1WiXdyKV#8E| zOJTdB#8Q93PJtz9t+YNTDBBm#1Z?mzQp-HLd9l)ln%dSNAWRkt`oPltu_L<>cb9I~W@I|X%JB{{ zgwIiRf9bk`KL3>vjsC>zUNpZ(VCzAVup+w2gC6g5@p1SyD1<=UA9_9o!_z{u_}dl%k}agQ6Cqpa`*-~0{*9O) zj#{QR3WL!dpqBx(s%p#ibu=y6BdAOCyuTzJkWVhDGKhuwO05Oz`KS&S${juH^mqsT zlhst@n7O}OnJdbOPFZf@lUT}gd}spBoXyRXWCG!gre;KjVrX@OfLx&Ejqe(r$MA!x#u8d)8Yp- z`~YPmbklVNzb1S4$^Igw9!;C*Knx6Cp z0uvGYe0`oOBPtBiZdQF?kE=G$R1j)s@h~Jx1$NR6}{cl>_ z@^HQV#<(3*G^cDNqHbvGs{)Y{w>W3Ey^nMQT!+LrXy~1S2cqHhMtnCPU8^Cx(gt6k zbh`@bnY*KaqO)fV(rBV|p6A$Mw_dPMjkwaYA*G`vS^3=dp{^i)nTPL>igG<+4pFs;IXY=_*($qvC>t#G0}IJ zS*yQh=;5(3LG!h2^6KuU=lrTA_!)41afV4xgn#KbX(z}|*cq`k?@tbVV&Nr(X1dFk z8TKl6t0(jY=0965B$#&A#Nj|x_#u$yi0xT z1u^TmSjGT&Qnl>@fDKBqyPpaNqy4b8MC66VQDF;Ha(o0^&7Q=`pJX+G^0Jg|$a<@I z&sTl2`X35f;0-A^rUw}Hh(#2c3?nDOfXfmZ{S#55F=SKKN&!#bPZS{%sQULu~Tmu2qH!s#Q+ z?-x#ovp3Ez|4#ZRHK-2L$Lb_&892jN*M&HB&14gCN1nCH2d?^o?V|?@A;dO=BTSqi zQ+kgGnwc^}JqHKB{b7PVtA-?{`~eTSNl0i5e|8&KA8RS{bu@KQrC$hroV-SOwe(Uu zTgWiqO-u3@&Gh#2(3}p8%pNHE05mnAw6%u4a9c3Tsd7{0!TuiLFtG>y79TI4ZJ67p zTzx_*Ls-6>j%Ef&d88ZI6RX^-2R^lucC;#Ep?CTasjN9GP)E@6< z(%+Ew5Uy*-dRE|xB(*u@o$>G2lpX_ZKLn3%zr&u z>WS_GmcG6W${N7y4l|sU+O@N?hhH;Qn+Ay`SktTgf_ioIcGl=l`0Y3=$(aw>9oAm~ zM@%`?ft`m3hIMyQCuDoL)(UI%By(f-*Lw94In=L}S698ljkz0upSw>L1zqIxv~5&5 zv?EGenrR`D1&eu32riAf&Zb0yzgNC5!?~EDURTB1$6Q2>um0s2Uv2b|Q(&FWj+Yfu zg=NTtjGA{>AEj+8NS*A=$#*0l^|3Uuy4d(B&fB=R*R`GUM%4rWu{G`3@+zU7x^hK> z>|(2h7A3$F18kfnw4ag2bVOK#4bs0s?hB_^!UE@k&x^y4}%D*c5)o7v3krd3@#wNkT_o{2JI zvA`KOGyO`^esOX$s9eTnPI*ti1p@F5CS4!l20mC0xN7!Z%xX-Zh-zueHtB%Ibbhi} z=`_msp2Mm?40Er+eW3FEie(C({N3E-dN%erGbG6gNt35dHCdX3zAe8yptMGBOw$`N zzx(x&qBGAkc_KcjJ?OFN^d(Fx>54hr-QxLHPCq@l^60#d4arA zS)rnmk|LrZUO^ELa5($D-R1NB{k5$aoO9mq^M0P^^}L?f%L|OxJT4e929M9$xEHpf zB-zC)Cu+1s%}x%XD66s2UzJtwU1#Vm8oXPrrkBEuT9m;|>yE-sf)_wE1&oXs7`taX zq${fss~}c5H{qR#+RTX5iA!Aid}1?&(>TmFt!Kiu<3Qp$7_U@fck_ZHChy^OVtpmo zmYQZcE!8ml@9z4IRJeoVgwV6+0t&JexBMv06rXITl#j_t18WrKL3V@=raF>y1y>B8 zo}Q;I&X9FtjD^%$C=d@CdO0R}M~cDbTPDYgY{#RcLIk5uUj@O^-Y86{`EMUsSl^gbvj8y_O+JMAs^N$V>2 z6nk)_eY#h04CV&hNWPiY3gK(?K#o^86Ih^Q#N*FE0SnBg{icl<^E4Ge59GF7@(eS^ z^7{)(`d`JwT9!tQZs3jqz=;7Kd9kN)thhV+Uwg?VjSvwG-T)rP|5d7PZhsHa-20!e z{Kxcjo4~SxEqV}O6FEBAm%9M_O7oA7$uq|Nhw^D{k%CNEIQc35q~A8x$4A>j$Pf1>r)gW?2-8&eo_N z3jT3G|Ci+hd?m+VKH?9|nOx+@p8@7f3;NZ6&%^w`?{?Z~XFo+VRqIr9L7wurU(5}e zL#uf@%{A)3A9?&AVq(8RKQwJrnD4x z?Jb4^u1?Y11sHAszP{4WYa}eB>!L^!d+Q2+e^VYe4D1lmWsFjAg@2@lD{Bx90%zga zALf8*Hpl1XRZtJ(X@BLGy%#H&i6X$rbC5@Gg~^F=sDGMc&j9mz=ci~JiYmck@|fDd zGk`6AL!p179P6&)jPP?Bi?kF-6ImP!HKJEmmTZGz(6@(NR4AnVd{Oo=;lLGt9bEyc z1-<29>fmst3aw96`<&;MiG`AWk^2&m9=R=Ks75z+-u=i)CyAwC(&v=V^=B*B!lfJ$ z^k$q}9DKMU%uC4jpLHOfwrx67V*MJsEpK?7ifsA>FpLyhZt98E?!MD>r9RkEhD5lV zs7e_j!TDT*P5Od`;^_cPr~^Y@(~uPqR%SgZm-~N-^8Zx$zrXuGQqCVANkFg1$Z^bG zp$1fJXnrYR>=TAuYOi~c_R_~)U-DK(gV6(0e2f1#(phv++Cn+&x2BE|Ja5a+Z{PE7 zbIh5{`xi+TMa50CKc$DsMRLJPDci|8rPwBJ_A#)2PQ0_9z21E}$}RT1n!*Y-RSm?Z z9{pW+c+5rPM`&zpD%4*--cOSn$QdyF6AYdHla;YWA*^pM{VfM(gz5AH0ufXBcJ zcp7Nj3)|(Ub~MC&CNid9u@xCk53lTn@DqbEFC z-(d`1 zs>%-(y_rmE+|4buTOe9}zPGb`=m?0g?nKSkv}68SuW^tq&8eJ_=*xhCkDfWqnB#16 zv@;rhAL*-~%jzfM26#hfH+=rM)yMZV_m^UQ;`>N4e-iQn^?bZ`Azo2$Y?_~Z9cyZX zf=D;;B4c=JE&451qlFnfwZ&V#aMbw&Mq@BfbF9I8Sz47YlF2YqLe~U^LMVVq@fi+g zOa5G|uUi81gnH*N?euG@$~W8ousCg`ww4hLj$uYc#m6%{Xm|-}x#CV8>!j+&U}u?H z5A7WK;nOvCBb(?dQf%Sy{Bz!Y^Vf`BuXNTjH6LxT6@zo|DQRf8n!lCw?OZ!6aw)zr zCYr|R^UU}8TH4;?TandL19J$Zac2{dcir`nQJ1!UgIA8Joet^=Z1ggP)kN>81=GDJ z^=V+tT}?(B0Dl`Ca(NeU+5VT9!$r&k?ZOFB;h2na3mz^f22^RI{+i+Gd|l!3K!Gr7 zv`_HCfa##L1dT$~Bnj*uCEkORP|x|Mh1K(l-D=BSMq%juTE?&|aYsQ+mI+qDlf@+F zIl~T;#z#XC9Gt+u?Q+i0o+%%OpBM}ev0QqI9U*Fx(inXqjYK-a_uB=-<5Z zJZuejgN+TTrvgFNJ705VY|->Tn0v)^78#j+9O??y_kpnvY%RxHK~==&bmB&mZKg(i zK@`ER5(fzV!*~|xABdkn%5o<};oD!!<*IU~S5>OXO zPO~RuLJ2$a_~nV(U>vF405wb}kI+9EsDEtxrm*bQ%U6hHP7qhMc)ItOY^OJ1&BA_* z4I^!@f0oCz;db07770e>Sx&3c^h&@&3&;O<+B-l61sMTx+@Nw06(QMV0ZD3?p>iPI zu*}4j=%4csPXDQ#bf^8#m9oQ48UM=J6f#s+y#I*@|;nL`(+q#`v6pSJIMe;~HVe=8i@s?-`p(Q< zKKm>EvjyWq_%%P>WX+3-hdLsZu>#>Yw1A2M$$S?T~mLCdO7( z6@w%93|uU}Tw1SROD4YYsz1Ii>qBwXhTCjJvW;f^N|hMeIpmja8=JT|F7_E^SFF`e z<=!lbwcp^xNSQbLlZw1_&~uh91j_1;Qkp&`#Qxw6krDF-3$FW^;2F9dpjO9iUyV$|&D;{LX4}h`ZO%9`5H~DU_((Qh z#j(^Kyqpki0(cMB?YRpuG@8MYEQM)O=4pN04KLJx zKP(YqpPjc@lN8Tj(g(&9?#p7VsfF9OmaK-#^ml<;BVfXP1^4vqAeDl$az-f;QL_br zi}C`_hFilW47wQuT_VLNM?F}2a<90J&d$-dk_NYsLjp;6AjL6mT)D8G|H-qq z-q}bH2s#!$_I18HdcVBjSAx+FyC={*Gz>r5_Hk?>zsI z3e&ToYN0t`ud$l;h?*JLYsJ`A%pwTPZ@DFdDCFMc=+U$oD%ZQ!a5gLxX{5t7gZ#fG zg7}&-Kj!L&L&{BgkF_yxqkVjWa~QW9uZ8#mFy#?r;TFJXfK^Eh%>_O5G2|lwl=Rkd zy`vcl+%Y7rCm$y*$nEj4Z}3}*1m~WXc_;~V$1V9o4x@38hMd2K#;uU6Pn#p|P^~0e zqq!=|lOH0O1`N77e& zlErYq^i(^)i#odg{0B>BRVX!&?gl0d;aV!)UfH+hgF@OHpVGJB-Y5-+?kfTUR|G!* zC2+x&pK>}e7cjxUf;Ew^5a_QN70eni?sNIu=jZT72UH#L=sTzKKMqvpiaMW_boLPvdlKTodx4AP;o>>*R%vCfe*;9igzueQGE z6FQp!Fi>Vu+edoOW!=Lf@g*aV1l(Y5C+X)OfRQ{w9f#DPa86Qg@tZZi&%nHcHCn5- zYlS5i`pbZ5L|^JLhb#z zXNy)&vvUdvlcxraxZ`p)MAR;(J@AqxjKG1hxSQyL=X=A#B6@uHyqf@*ApC0vZD@+0 z)lWh`#AYULlgApnYN2r`h4bffJ@cAPme=Xsp+)5+!XS@Vq_lC`bZFhQ;SJUl!}Wyk z{&_lD4_5v)H-+PZ8+-h69Hi4p0#Jsz9MkuS1witc2T+;G7AHq1$Z`VX=4w{_Yef40 zkrh5h`|jp4RHTs zVYm!P9#%mTZqI+(qn;2@9`V}V^6YfUh!C4chX7MYcn>StX+H{Z6i`qU7yyD1^ta;> z^j9;=y6@Lg(g6y0;J$i{lPz8OC2gG5ssv?G@Z+Hn!Vm`P0ju%%ms^T22#H0kz7pS( zO~aFkk~BRGT!htf-#M98v9{bH>;tIyT3&&^BkQ&HnydBWG-lfSPk;hB(Ty?Q(01A} zqVy?&05ih`$KIusd8#gwBFR?NTZ}+`8)Bk{&w?R-_5J$*bfQeNke&o->r5R#6XYVDyH-Aj4P;pQeYYKn0I``c zC|1g@-XrxJ!nfm==_2rWCtv32UjC*x0F#0WFu$34s6~i|o=uRh^7;MUDBC7u zge-qB_F(PZZM<1*Bs%8Y2I1fGBSS;yK7jp>|NTU8iE515kNG-i{?*q!?-(DyOi)!^{Lfpcmm zWCQ8pJu->1f2tFP?X?hC+DWgs+g_2^=_B;S*7gIb%~z>+_K43r&2m%1;7RR3v6NQ0 z=D5=XuO%K3sEW3MX_!?901}jdE(OyyfI6wwtOV)~86ubu_hTz+L(DCzCW>WnmfpZ| zrkv59Y7QbL1m3XAQ<_zPH0xgC+>f~~fR^%Y8RHXluyS&=Lg#dG-nB}@%uI%4dh1*g zHcPBxtWLLZ#9L%r-rw(Z>0Daw$=xvAxVi4E!VO;#OX)4l89#B?Pz&2^(q5FyyEzjJ z(s!EYWxll$3*Yf1+fxsEEx4h+MM(3t&PkQCyZTSG9{kE+wE-jn92qosj2vg^0*X87 zYb+v}%LoZnZvr=WRX~D7-ZDr%OK+vb;i0}eH@ZSNRm5T3LuZRY=gm8YkwB=W>%eXj z;58dLUy3)A2e9M#Wx5~1S79Lj_f+8Ln>O|`YT=|Ul$ZmrN|p3Ws~ENJBd|t zpHNdx_oi;@;u2~awQl<(^McZi;~j~;dNN3NuZCBfn<(O$y<}sA<+)ODfv^yyJL=Jj z{1E?IJ!+|sw|e6|lh(#9ni{d{5hF*qb1+-#w$`)OWT-HLo{+hwqPfygNvl0%@FNEF z8lH0{AQ8ICAb}+Q8YJ1II0CW*F?LnVIJ#m_2hrg`lJ|u5R$&C2XnkAm+uE?!ti#8D zAf$1}5F{qX>hG&rDuU*w|BBnTP^Xvu60oA?AcRe~KQxKLhKYA`u*i1Fyc?`t6+~#T&33-BTrmvzLcid8I8Ru2| zb0~Nm&rCU@T4dtHv6G#9KX%#NcV~Bm;w>{=T5CW{Tqb&?1G(z#?fOdRRmhY&O2HeB zQ5x2po=Q_zNVXO~4-(JMiy&&|qDK%?!4NF!G)n04t-oJ~UsAcY*E-xs@e=Wsc#_R1 z{A!XSiT)MEvH=E)itmvfQ|06M;f}yUPSd1SC<$}S8?gb0VzHZ=3HsG`qPlxOKT0pZ z?d{y&W;>#}==i>jSytMWS2hwLA`Xp08@N3$V8uTCEZQ4xG5;oQ_5jDCWfTVFaOH`23Uh~siON;0e%jJ6pb`dczz1utwW4i6xS6% zz-F4Dp7s>`1eZ<^G0b*<@_7(iSh*TZ+&d$0BTwGE&g1Nf(8`yEPm>1?r?jd21)I z59A2NL8*B%z?w7TB*H>9p-h~hLDz+ObX_dxg5Sr%7J4%~AV~}kggWz|)$Q$|)nv;2gn@+XIBTMXdykIT zkWfQ*{heU3LczTTE-n+ z`v0170D$}e-roxA$;64*tT7oj?^*D19BMeB-uCSo9C9E%U@)%rG^|Lc=FM@1;ybl>b9TgYRl{NgB7^aFU41aDs?7Miw($T(A+fSS{5-% z66^ma^UIgsLH@As^peZdQPbt0H@(=aK7WgGF`)~n#lyG9A#~}G2LoGOud~N=*g9iR z&{dv%$gzPl+q7X@?>lbhLs&PEyJwfiE^-5>#l1}JTCz2ud>mhk5yqi=VEAD>$zz;q ztuq8a!c+4`K(x#uT_x#H_d-3L;E5;7M)Ch z-*Mb%tIrGx)CUU$3h1dO52&QT0=`BjKyRgt8;sOS^~OE2VGo@T>fWBSRS^u(gL?d; zV3XM+O8a_`CFu8xUBCY*J5AlGzle4Ihjgoj`UVbHmYFj7IqD$8z|_6BsA!lyQ0}9BRec8=GafM)vfr>4 zLIdn2ME_7MB@lx*pac46!!odc&w}T*ZWkLu<3`C4_>YC%7eGlqHvIM>IMsh;&|T_j z5vio1174tBIt-~qfa~P^3KTWZ|C%B8MsKTWbX-RE=LIG}j=CSrwaEa3y#7qkR@k4b zufw0x9cEw#D$pRzj=FcIrNK_d1UdLUF=x?KF`B`VP8X3^BouE@8i0R^r+T&hYV4`* zwp17o%X@vyiXSYb@v)hTO^5s7_q?8rNQ;M9&Bw1@SluwPCauoeumP_i@)sZu?_xse zCe&rfGbulz`v=7&6TQkUc8th}YodC51Fb%+?VJhUF3i>xwT^G)})@)xX~!m6T4spFs9PV>F8Su4wS};>6DxoMqBH4qj-CL z_l6&C6m^X6Qlqi4xESlJ&wgu(Bnir{#qJ@S1UdK>#>DzR26wqGvg~4N8fXhUSxG?7 zSROhR57Mef_%c|{La9_&eI^L-uqpf)ZcIRZ@PHML^+Ze@jjZ_a;rT#jxg~kW*;pe0 zM*}anSfz|M^k!nzX4ztltZYxx$4T>zu`k@kAjtO0@cbtd}$_msY$Tp_!nEeVu(BPPS&btU&^m#D&d4Bx8&Ct$+OT z7@A9|t>y+PxmObI;%}g9B)BKw&culI6pz)x1os~AjH67|I*@tLHNQnAMt+NY-RsU% zcJvaiqBi^rSA}1)^qDPgB_+m3@ps;MF5^V-v@Ox=z7X>nk6*#R=*eiZo>FzC9FC zwe#jhG9D5rA%BgviWU1Ul+oR$Z3f=- zD?YVhD{1~O>T_JyfM5GY;AT}y*G#o-oFt+JH_&y+qNn1Wd7rC+dMUb5>6#{&=wlQB zI5m&8QbUW!oaaaf-H(3Ht)^wz7NP>F>!V(BoSiSAn(e~ehqY1OykUME825dsijwI( zK5)@JdhUjSHCWp?R~Y-a8_duG9~3RbGzhzv)b*fH-iyShp}X;OToYqp`#;Lar2Vl zs*B&;mXq4~!#jIONq%4JZ$N>>c&?!%a4Z#H$G%oLGbEsscj&JfCkMSV<87V7Ne=Ts zWHf^kpuS@cD>Ypr#=SrLgq_OOL4n)0H8a2Kdu6>MW`N5` zFBN^Uk~oqdnS+5mdp>l08yqOc%L}Rd+T#=bmBNP3X9{9Gp*4sh>eNwfnqkQ*axDRAgSYD;SD^Z}nDw_F$h_|aK*d7~oN{1I2nJUjlV!y- znPL{Y0FC>5rbiX^uNzOc3-JC6{GVgASL*aAnhnGR*AtfuSSWK~ho~x$WSQh?Dgm^E z$G8&dDHq?hbFxzeHZ@C!TF!dkKi9XVF>6|6bkXzaGssqKdZB1I*{b=L25Z33_4=oU(g_@mC89wcA}^ zdl&QNG-F|i7qm6o9#sQkjRbf|o*T_6s$y}O?qL5zoc#IG-%k#3-xPjb1tAzoruDq) zPzPGiAe0SC0mppkAE=JFwxvy16$1(YTeyS($zGuHHSLywGbz3nKK%)YSg~T!Ki1Bc z#Nb}mx-~kCOPau<51FRjcnE?h1lIG=iS;TmW`>zeD3=?y3soecs%jxTk_e9ew42fJ zhBd&07xap-!-DuL0xedq$6O|3q4X8j29>iq#o_-QVAA7nAHH_D;75PddamE&9>*1Z6gcZbY z4M@EEj>LnnhtqmD{L48i=$)2nA=GbmppkwDxdyb<=Lbyd6SfVFwQ79N9A*g~>vyb_ zd|zJFQQ^)eroG{}O%AL{np$0PQH{SA<@6=nu|-&Y;ae~@YOh|@2i~yh-%a`dw^ZrhIFn&(`Gk(4G1@qp_^=lo3&aU*MIj-Pkdvg3 zWCJi2yC~io!GneU2s!P%#rNK(6o#BY;TrGBl4^oOT-H7sY+EHJvLTl6t2?Ad@ed(;MC31L7MFo#QTd?8h^_>N$E-PyHO z7+ZNym)1m{@f>+m($8KbYxK2)=MNNU2`Tij$|XBPK#KcBpEV3mu4K*BKZ659aq1jo zd+*p)&ZbHRQ7|d;*f8~r@^EP;{n=^fe zov-})#is#J_h8q-l8Yg3E&S&rkFdAG9L_Vc6%7yFMnlkZ$O_%hSdDm@?FD5+1`D-U z+N9<0?xiO{hLu)NPXnpL73<>ie6F=qS4eI5X$Ow31;J;oZZ1la9H^#}mlIaqrdK*H z0DPHhM3Xge(bIuZL%71((v}}e?2H%^5flAZhlU^En>Ge<@XBRCW#^k2TO{8pese@~ zTe#HRYs~cH%g0ta``URSq2RDvH151$g@L;v%`;UL6&*SBNn@-A3F4KTG$E;UU83a` zDp|S+Un8D>mavgbtcI>d|D3r%VTV1JOH_1SwA~++yS}NMBM10qP&=P=1<)rExgwSbHJYz03@(I-lnNkKyc(sh>A(^Ia5!|`H zl-vf>tr<*GuzTL>r6sW05U5(vgV0>ZAFp4lG!m~sNj>1&k-CM2`$=xWWs%}wpoXI( zs&nYs-AB7Rx&}H^@4?w1g$Kn`;Ldgw1gzz}>y8f4K#+o=V!fI8wRmtPk=>7g*UR87 zgse7oF18LCqK7tX_+a^VPN>9y$P>uGoSk{k0Bn#TAw9ttRb{BeMfSt=Sbxy)gppGa z_zXDvf1Q>Wc?f4_w#fpxRqxJVBxYZ9RA;k@Js2}vJW&H6x9Q#%2wto^P$<5`tS4cl zLUC3q@v47mXAR1VpDNqzSl1ZKP^X{>{4%PMa#y*n^(-Hut`_n-8&R%anyjGF|Y65XQ%WEtyN_ryXK&LZYUkM)ZDhS<(AQ-ouZ~NzqjWU?GYV4Iz~?OZM!#aj?F1uE^Op;8WpO6Xu>cT ztmPYh2^?yc57H};rF~uLAtA*(q`awY!yM6_ijRdo+_+9!dePGSp;^&a{}7uKXi&$z782*J8?0LlIIhuKv8T+ri}U@UwRg zB?V8)E_(YgyZVAxndRpC#T)_q`h*zy-o6?qBJqH_5XX=@vk46b2Lij=+M?EZZ5y_Z zQXq5{To_wDSl(S^Yxo$kc>M}w-PzRbfVjQC_cACJke8IVuNL$$49=W$7Pppw^Q0Nf zmteQmd=&)ZMeW3yp|4(;Jdx&)u+xc5mT$g)XHD*LK-8lB+-+hF_2iZ{kz7K<*gXth z62GVmh(;T~cBJh@d%5nFLo2giPN*OiTqxBOrqBPd7EYw0j=A#8ea}w5PcSzm_>+!G zC4JjGc*i@q$aYQqJWliMzh;c%UNuj;Ls~$4c@v2$Z?&No(*n4Py*LpMpp@pc*`Ja7 zYDaw*byL;=Y=oJHcsEq#Qf-7P?!r&OozP8NYx0qHE;~X+TsRh2+aFlFDOvq|Rq_D* zi^Q>E!JPE)53W1bMurrWxxe+n%!qeBt`7|oH-}EE^;s9{Kc6NIM0#_zxX!asnZtsQ z5Wn=K3-hS&k-_fON~1(3gGtZyuX;X^cMdu_^@75d z8lI_l!!KyuxKUrS`biF>e6q#+bg8F&AZFdNX!%JfwmIv zC0N07wxF(RoeVuN_L{R^Nv!i-P;I`Z+4jYc3XCp|H6uCW*7p(!-vVtk&y`fu7Prc` zJrn1sD-A~3mpE&w0EWK;keuqR*fL6_x==Z@30sHSBZqS-=LM3qgm*|%9cP=8r|#xT z!H%A@b*ZC^q&K3E-jMnIOsa8@+!k8qicgL#U!(tu6F^K{FR1ONVq^9t{ricjAJ*mu zLnR|{AAQ4l&9S{CN0#vu62aXP^VH*>f?n0wEdzDBJ{>bbsR=b~{2~cbDEZ)Zq(tGS zey#kz>B0wthIH)mBLH2fDB(3b>+s6G1JQWmT>mB&%S*YcIJgXdcHCJ&R`VxB=p0&L z`hi26l-Kek2UGH$8`<(Nv3_{T-u7d;ws5GVQgIU9L|&Rc7I{@b36tAwF@0fdm6+jOhD7mq!& zkIj4KIVE6zre;KyM?J|es$zKLbCUY1LQjt9fO|1wO-2ogwPocgdMa2FA3}AuZ+Eiy7odQ zU5L0o++!6D$G3j#ziQ9j{o_PgtV3u(Yy^&M1NzTe2wMcQ_A69G*E_}Y0Pc6n)EgJz ztK6@w9S?$6(X=ikhgnZfYJ5BtZJEGNU_>2%h;ID-ZEs@bzT-*Naw%~!LV7)7aX;z0 zK-*K98U;&1XXtb6v;)@A>)yBJFBzxO%JT|R=`wZzr`>6x(=*8*f+@^PF2H8Y4ID36 z7l%=&VP(`xLggky#TvxH5 zMb=%snGQztMC)%v$21GhUe|hlS=4J>x3ddLm(WPb784AFihA7FZZ?iPAD#_dX0dDG=DJLK zg%z@cM>A9~lBC!r$phJ(48>I2;8W6BuMl~_V{|Lmzo+;C{zZMaeOWq>)ToqC1#1(Y z!PPMLjNa{MP78H=54nR;6W!Lv>ace74eWt$dZ#6BwBVufwzf(z&dV@H0*Kv&yuR{a0oDwarST58Ml7ITXOuuAbCmuh-HMc3x3f=kES16=liwQZjDK!Y5pYWL zYo$LD7g4;|;IDmE_LXK1?(iyW$NZQ?R~xpEHQtr&iVQ@Reo-~6^p2I_gnxsz!aZrw z3$G;Z3AkgNC*dc;#ifLs3=`Bnhr*Y7l%<__bYTVs*`A@`Bss}>Nc8Tn%ezB$iw<04!lHDrDTrqWqAIUI!-4}Tdlon$0pi;<(skXm@T<9waz)^BKrfFOSqMz1ek^(k_m+Qi7r6lAq!w5)$l}7yW#-1JTOrW^ceB-*z zwdc+6o4L)$x4h~PZb9zK1=(e7wv!%qr5v-Yw-+8IVWcV=H>KQsw}g(^r>U)4BIdGy z9spk|yBS9^43NAUb2dZmTh2Cw7aw)NCsdx)xw*VTzxlkdkR>Eu#utxO^E-;bke>5w zs-1#S%bNRn5qqnjd2wiKS}R@iim}?JB2Zm9>Gda9bY^dCFyP&o{<} z)r5Xe6U0-0WRhQ~fJGQdga6>?MAS38NwB%Iq9Ml+OJ@GX1sQIYx^fqEAXbtU4-l- z>10bR6O!`*@V`QJ)Mn*uKpmPh==(ao^#6Bt> zGK5cm)-(J{@Ml^=+TZ2wIg&QdH+u^9UWMGC{^{-t=^{e2>2F!0pSG!Rq6utnY0MLs z$glz*ti^o$iLGR}xt0(zP4{KI7D5m0}CEC3$*NF3qcXaA2v=$TH z{~2f>6~G0k+XQPQ@-t{!zfiTIAS|gbzQi{TvS6q7s0 zLFEDE8?fS;pm_{9b-p+L9Xb0)c3HLB`~z%-{wo9I7yO$Xi-r?`jpQpnaS;)4;Xr4p z&+8w@Bts7X4^FH2v<>T^*Sz~|iT-ruzbItWh37FL_qKQRwr=w0+)6v3KuyDRAcXs zX=zZrgMW8kY_S*=Mg{Z`{a7tF^WR}ZFC9rEI#~OZ?xNz!_0jO2SpZm_sV)RTn%bWH zfU7?XF(+i&eI+=+$1^jE%>0!j{u_4odnDmHlPB>q6v6?;%vV>_J!vtWBA@K?GgRC% ziKx0X)M>=QyQ>r{X;R@VlfpF9H!-k*Y=P%|JrJ(n#9PVN43Tyf?iawRNtg+Cras{A z7jnv&NDGXuFyifR`2L9W_U}7xns-%%9oEP`-&~yGpq~@H$Fr=Q;IxtA_YMZuVWAE_ zhqbJif}|<&U~Ga!8_1M&=lPw+ekI9JHOqKMeM9h3k-HTn&;lP0-lD$EaZ`_CBg(nH zu{c6&DZ#;S6L14*Y>WaRBpy6wA!&O8#x+#`YG=LNdUT{w@V9ul`31}iT>d23{*$k= z{(aCdu3wDH-GN$-StcA952(Wj>)gmikw9*Y zkrK0uE{pQSU>^o+T&0#X!u&oOF^ihzddRYm$}sBRoO{)qR5j_&LUAugH-fUY!2!UR zdl|B9+9lLb7l~Ri3_^YfXI2ofR;@QBbU1f_(*a0M3qj_n2Q~A2l-IB^uvFrVRapitQ8nN;Rj?fu)5+z$pNrr{RJq1qKXKUV$TxKY`J%aDV?{ zh{Vv~hiV7QeYGpi35xCn=^`^7j%RM55GPkc0K9qQUqiE3bSL*h3&D2$MBWL0zV{x` zN%MN`w-)4(ZrE$i=GN?16~9B^t>{99@#;ZgHQto=9fiDihO{567xI$+y!?rB==Xa& ze*7G+T1hta-#qzFlU`NY=CgOo*RjOWvEfc`iD#0v{WiT#LO4|E-GtYVp=yUfj$!Vz~JF(m)tfZx~9e2x)Z zW8XVw$VkXEL?WcIk#%ClO}Ktlm{g2-Oq9Si%b|w@r-bY7#Eclw z(94R5%8T-l9TlJDjTRgB+LL3)#3=nEjI2NA7g>A%x+PfM7!tC_87$P5&{@7(_k`R!u-FVi}WvAD`k5eb&d8{DM$Dmnp}>yd|-`vD1178YFx)@sdSr2d5cf2+cE$ zGEHG=9gVk^skurSAXV^%J3Wfq92>*3L!#5P1a0V_743Ez?%;*0Hs|*4Y4(3oa`i$sAHyHe*i4P{rR?4szw(e9K7UR_v__=h`en7=3&q5Ad z0fAGOluS^E*l3UXu->C5S|gxBxcWG@s*^v5tA=lI=FkM?PHCOx@M+5{7d#>vOmxjL z8k8_dzH4xotV<$m#xc@6j@OoWv%!0Z{X!r2qI!)xu~IvHIQov766BVCBxJiN=6p7* zi|sM%!#H>e=y-5mvzd~g+2>+&`MO1H?5jwY{h}LxoXuLlwhu_87o={>nCmpG_d*RF zg2-7_aQzaf&^%tW@Ft;~AR&sxC7hJs^v~OQU%zM$O z81pPF1;VJ_j-gWJBl>IX@RVip8I-Xw#E_2xL)wW6`Ab>R?$D#TA>5R5)x7G7s>qvS~@+m;?G&w=tk| zsgl*sv^-O(Pj(uC*+YlEH57a%~+1-%sH7oa3oKr?ScvVj^- zFRjZfxsR`mR67d%`ST)`;k|o5aJZi^bA(V~A!y`P(e9yTuJNU zlb%TpegNj}@{Wj!XjVA#lD`1W8po{J|OdvLh;C2$~xuwRh z86ASxnX?gCX#wzv(iA1=sKnQUBaupx{j7kJ!NDF6z6#yv=p=z~LEdsDx2A=99Q+aK zW~q{gTGDLZYWnYa@&fEuJ?%bi-QcxboVhfaRaIJEeE!zwvM;_S)CJns10wf=)MAZI zI%vsjGji%>u5wVaaK6fv{U@kEi-Z_bA$G+_#4g-LEth z7I5!7lbX3bAT?L)1taC~SwD0$!gv;p=t!uASW#7ZJO>E`E#<2r*s?P^@a4yE50NfV z=j#6eG+DSK_h4JU{=V}<3ZZAXGb1NA>$Fy%om1RPTt%Yz`^^aH+{%{1*YS!ps)xC` z@@$&{;OQv-glmj1WxZe#H%mAA9y8nRbfKj&c1KinEH6W!#`569N@9GEuySo4CHyeM zr?rwG{eVBzOg$bE9F>QV4^GqE{v1u#gT%%H>Id|E7B(-XX9xZ=m(4&|-0guAtgkUH zmxxy(I6!_ZNwO9XI2+>)D_!ysyqhM8+HHhH>V7slv!tSA+42n|`#+?3?U$gvgx#W; zn08rCW`&4k!89~jb?AU7JROt+O^miJ4*Qb+xZ_z?2G%q~)$vK* z&~}Zw_vn=eZQDw7%js_N4w}KBWB1S8aVcGq7nO&OeTMtQpOkVCh@Gsm7C3lLpzxC@ z+yd;KSST+k_ufGY855k1ztA2Ivo86>GBog>W}W%7#ZZ4AsJp(5}e2kNtTJw1}rOeK~TVR&8=NAJv)NCu`<`gH@(?DgJbr;4estI&(osO*`ua z?zD~;Ou}LI@K+--zqEl_JG0h?dj;^Da{$hzz<`2x zS5Z+-n~PX0c(3vEu+=UAaIaaT7^`szjIZ1t+`Gufi`&YTm^6Kadn58~R=6G8@O0m{ zeKWqQI`;0+V1Rl3j*bbR;?5x=VQtr$^AhPzX5`zPGEpc^+FWYY_$d@F4J<4ZgaRJl zgx(O<;aa%quyJiHE-6Ac05um_Q;+w?h#;oAodfOB!>_&V!lDSq4d+6Jeh-VkKhO^N zJ3BS*a)WfqfRd0vUb#&`S^tb3bG~A%uRT2%+A@mkAgNQEa+=SX z4m4IBP4f+GjyxHyu!7UbKUEf!b{ajh*ShjM&iungl#M^1@UZL03LjNJ8uYR^Dm$SD zd{w1~Jnd6Ew{HOueDF9FZU^WFC-a##X;|mQQ-_PAf=x?C`sAn6_-`CPtpiofUo+%J zT*>q>FG=UVCjo=V)6U>db0Pv*wmDq`9`2-K!6z1t z8A1{D|8e!*0ZpCV|Ltonb*2?@L$t0cA`TQq;J%hxDil*ih9IO$6%ip-kxgz}DyXQb zT0lXFiV&3%kr8D@1w=&%5Fi3V*dc^K$RgwB_IJXl-{1Qmnsx7ep686u`JB(u8EWl@ z!qekHB`Wobj&VePxIpn39#$hy<7y+IS)2OyhG_&bVzz*TuU(=SEOfE1+E9~=6{a~m zx#VPz1#s)yw4|6#aXKl@(xsM#A}Ry(ZsZ~)7tB`k68j$FXTm}aD6xt&GBZPId$=&W zn*l|N?e38Wn*;9D0WmHTxzhn%)J@bDI8rqs_|Gt+3uvYxG>pj;rq20Uw%*!3voUR> z%dtldHsbVa*b8}b_v{SDXVzs2U(fry>}>*bA5ZT44i8CL+owTE4OtWOxbYm24@OTO z4RUDp)6n~z?~O2`%qTL`8p~veRc&^;OmLlCgB}@j?YRuh(Du-2$QU6(0~*w%w()Mc z`^oTNycErx>fD;Z19uLS|1 zY5yNqA-X+ym*iCpX+8Dx$Vs@P8J&PJtfCUfP@-y0EcOI&1TOAnMrAkW4)*~HLS(41 z-|pCqzrK3%;_3wvRJtyF?jKXw$mI6`$}#hQV42X9!B5)AV9=olYibr4Y`+Nv=CE0D zskfl$U*+Un15_qF@ae?2uP6b;hXZm;x^uNb#D5yGh2uyKTWLLZQ&+_%K`U3(t0n1= zQ-D~sF_qkp8YF+ITI-Sk18smZ1?hxqWXIAnI5i0i5ntGvHl}x7#O8o?^tQ-OiK!jF z<{P$_<7dCys-Ggo{>tm{2)lQ@)e-_VV(CAh?0PWqz=Jgg<7!K>DO$b^UnFYNxSqey zRkLxnB*O$cGvV#0>@0AKT8u}T4$wYTo4BEoK*%aZW)77%7HYzD?Pfr2zPL%)WfrLM z)L^uq^OEM$K?w+1@?LuC_&Xv41QjINw#8=5yQ=23rZE^jRw2+GW8831WzQn~A_8>FV}F!}Mk31YbZJO{pKbvDuUGOZr{^TXNT%NEjHAia>crmTY<~z7^r4Ra;Q`Ao7ek5I!{I< z#bhLsM1V?}gpd&orwcOHS6HT4)2fjKMAOmqK1hoo<@k&VUYsJ0prHdmw_Xny5hdRx za|GBZtblfkH@6Ij2erY@65>c0I&O4#VujnT+pxDpT}Cti`Q#Qd(*O2_WlQ#O7G55F zO7_nu%S+JL3mGRhv+PS`cUkZsJw33CY*R$De{A37nbB+?cRSsA<_(g)zbE5E@Rx9G z0UcvzncCjzL36iW<7vMko()3Dvhrol6C_kx6k2JWbBKf~0Ik`Fd>w=}o51P$C zU0qmtq4ewgrh&pJG?Vztp&DKK!r|r5o>gIC?X74j|wb9RPBMn>%i-awtcicqV2v z>tnx9O(-zR5&2J~e~;O;ZpLM6(|{hzl_iAB+ejBXmcbN-ZRldZvL%m4cQohEiO~8V z2Iv5@hpqRD-Nm0dhhX=5)K~Y3>A)99=r0QwXiPG5P(M#7+Vu5jhlJ^p#d2fQ%*_m| z`hy+;s~Q|^+B@Emy>p}m@#6W3R_R0M@`N4j=29)K3c4%4L8{Q1s?756n0O!PnmEzc zen>SkBzWdK7k5Tc8F0u(QkjR~(w)I`?WucZ`?DKN+ zywC_2IM;U;#OZ$570|v!tu)rGZ*FuU0Yic`KB!7s|G3Y#CI8pq`(gJ>-xuCMyWIAX zP2X#edpY6}Bqd!FQ4IS~Fq&3d2G-%rrSvCCooRW+%b)xc%-*bC`pU#XIJ5b1^k{fXrBi#;?G^jJ%1`6~@68Z*vN2`bs||BD_$1O4feD>Oq9b`CO^ za)6&gDri^X6C~_%cy{4M(3wJ%H3C3O`am=BIr{eL>BaX4yHAa<%m=a=_aMcwF?d6o z5w@eUsw2n@W-EQ$=^{=hDUI+yfoxRxH3BD z1bYmEg8-h`{ygf(jxO7zja=e(DZlN?Q67$1WU|#W+|dt&3Ik~0RXj_a?n5RVi@&{o zO+oV(FRRR8U%|2!-5CZuyK7CKzb%~+<<_1N@4l-b{d!s9P?4_c4m7HCk>wYVu9fv> zY|*gfQ-}+gnwzFXd;#w_>y{z3YrTZuhQoF;p-Zm8xCGsQk%3>BH_Q93d%pBQ&&j!R zUm>wo9^Q1n)R!PLZO5D%?k3jC0Kv|~Thd#iaz@-B-#G@F<|AN@tPd`ve|aayG(tF% z-$v0!>cBVu(g-W*48b)~Jxl952U>FGBa&l?Y#!wbeQpq{o~ep^=|O27kEUEl*Idca zpp8|)?PW$%Rm6vTD4%CO!d5qC51&%lERgMODzQ&PkNdZ}7Q=J@8MqVJWx8~uidjw{ zb!_T-(kfaQ@@>vV|Dis%)4kJ2cQ7`-G+SR!d2$=!aE8sxNBLdxDcL0^b7Q+g{(x|0 zK;xA_>q0*B8t7OmsfkIvi`LDu0~Z7&=(61~c$Y$%S>9D+2ex=FmD|lYR(SvtSWPuJ;m)lLv(4^BOqfzic3SrLSg5VPdUvVLlq;f zBbWv~@1Jr2=XB40Wg4+MkeSnwN)F>(@6WvO6y1>1dpl6PiW|ps+{USWPv&WALi z4Uz&rjN^`a`Z~$4E0HK%(2+D+Rit0FpHghc^7aeYPx{SC_ype3+G z9G;Q4|C^Ey5H7$i+!)T8R8Tgjh%zT+AE#;es_c}_qr{hOVngNn(Xr>8^T2i8*2d4} zNB&op8PW6ABNgdoxfa!ixJ%a6S#Rz6B=u{E1HJf?m_m-~VeP9|TCc0D-;Y-|DU3uavVw73hCaStI5a%%NiKz&1|`T({*hOaSFHm>Z|F6z?TYzOI7tVijCOkk=4Hny<07c!tEF>veADf1mmTmi0 zu_-A%!b}%5&!e>g4mDYApsc*SeWA&Bn!w}-UTBq|#T=ZuV_j@l8yuvIx-d?;dCS-f z{RvuyIjK0EI@6!Hw&s|>^SRNb6_&Oeno9O$h%~M-txHctlo;kbQu*5LtPlDTInUbU z_32N>7k=|yqWGq3tHXZR%MGr*9{0PO^6Rz5uUfrMweB3XV7K-(@IwY7f|g%VKDMhUWN1JaG-=7e@^xez>MqAomU6fE|bn zixc#^k;_!s%$~kpfSAfF6t9mxO?FV3oerQV*(zC6WLO$1vsA&zi9xbtzth=wtW@+j zk3bgc@7smSOFgUTqg}f5IzaZ8BNSCiGhiWKj2fpy-3DB3#j9Ttl5b$D3?>pf%mW~P zkL{XvUD|8au6v$Q8Jr=q5S@n%&eB(UzHMKNSvz;%--7paj{LTb2-}3iuXOTl!ymGN zBUR`9Rd<9V=*8#up%rf=2ig*=4@DL&!=QzXc!@6GYyD{e9;-CvsF+YufYE|wY%YUk z^N`w3Uc~%bx*?JvDR$DD@6ZB1Xp-7AVE#pBO~iJ(d7$^jW$yI*IN8Z#8GCJdvG~1= zumrCsFTmIOq3@~dMrkc22nTcSFwDbiw5Tq28)p=c@5U(<75F6))tin#($UI0=BAe~ z{&C(PwC4QV0f8xQio4k9p4bZt?Az{TWuc3oW-Z7)9sYg$0={JCSa7&oJov>0{1eZ5 zuu%~nwP109=Z+lY2_(Z_mTh}SWAbnmN{h$67a!Rg>0i^n46ac|xMCVa4J z$Ai_{g@#cVNCZ8lHBQpEf{kAr$-xp-ZC6Q3-cE_Acy6CqOzHV00 zTKqp>9g-bVoyE`B7_zZvI0|3}ZN{;`H>R2(Dnd1wtd*}|kY;X$sxbML3!jWKJEGs@ z%8=9PCldHd!DRwT#vwYSda`%vP?j-LMI`O^#+}O5f#2h~`z{~kORFg5l-KIAwLPc! zmroP-XM3k*WRF_WLnQawOqhw~au`D_c`jY3q& z<*Q+E&s2IWNS~D0aq$)~rQ)`$@!#u3gma=91g8L9kW8+PGxDBF+zty|O_JEP9my#9 z>yinMK`_jq{u~1;LhN{&KUmU%A_5v*OZy#tV+{xZ#nXMqjK-yXn9!_(a>g=mkC--v zW$5E$kovu&lr%O7bpS)m)-cCMQcr8wq@;(h`xJh5T;=$LjC~G2%@qpUU|e+_`cD%U z8^f`SAuARDF#spWA4=A)uQdl^fPS>hkd-GRFjn$4l^cL~>RwqYD}c~k4ptZGNn+0C z@zAI6C-mYBnI5(zlNz6t3d;FnbN+4}0{!gqzmC!8C_E9IrP3j9vNvkkCBywR6b$vX zs*V$ug>(_lg_4y1$_viOs3JZC#BEHA=QxwuBq8bwllQgRt?M9FtQx4G>=0r#!6U-s2S1(cTRXZKmJugk$T@rChCV|F2Q5%SSWI13zx#;&g!1zp zm##-q*&qi+yH@9ca;sSIO+|4v18^n zRq&t6RKlUtVw}hfj4=+^EXrBlP*9v=_)|d%2*B{)LuD5uKZ%|Ff)O2rktN_-0h{A7|k5d-%< z$TS}B!I?X_f&zRI^$-^5EUG`HjU}OnjF@4=d&C%0?Y_px7$0XtAdBU-<#fFjJs>J;t;AZE{8ucYYxbvfS zZ3PX?ek%X3ZFJ}pVpSpv$m~@Gr%!{0LL~*($J+3IlF#LEs&^vxZR(fYTlVQq>9S4a zA5D03@?MYr9_K@f^{T0A=Y$`u2x8 zq1Sso^nuOAZ7KFN?M(tv&}QTK5}n!2)x?&hj8#NBazR0&tDo#(z98?5BL7W&aSmpC ze!6Qi=IWCJ^*387AdRyXzMJ)BzxSv~D-+2YXse-=f; zT&GH6#LcL+PY3UNUU9e>Mq)U6AiPeZBz6FmmOIp&WyAU5YKdGmdfqMeg=W5ORLK(8 zivj<9@@o{4#^I{n?vz-(@J5Pa9kq|2V5HP?-O5mWSm95hblU`bP&5m>B>dPFFd^gz z%<`;~PM!leo`5ACxy&o6izI5*of7*$_2M=N`kqgE{>wBxTxW^d-Dcxa`^#FtwcG@; zhPJA~KPfxFoAsb@0dc0vb%yN+5Qj8PhKN~P4AdqXiP@uZRi)0^wP5>~o*u%n!%;W( z*5?Oog{}HqjwT0tMf)oqb-_7X=eK11L6;%SO8JX(7cuPOT5%@FbgN|)KZ?m^kX z(vtkqIRb1YSwGNV;AhBA?OCPh1{LFl`(@S@ugIdgS!*fhy^-Kdj3##;;B&husGtieoQZ@1NWY&(Rxrh&QbS5ZNJ7WwFnsRfynwYwtNe!V6SKTYTY8g zNP?-p&)9Rs?A`^NVb8C*qSwY#1Tn!>Hbu6x$HEiXQUG2UBWw|479+zd5jOyQf%%Zi z19ZkKCA+e8tdcY%Dph3PwAs%u3-$BG?-W!Il38nq?rv+o`x(*5db@2kCQ| z7;U-@{;c{|xTi$5?6sItYi=8O9gUXCK*olI)!hMGmG6FSFlmE`B4qq5IBc^Rs8H zg1kn3+3f|sxv>mjj)V@R)>Ot<{;9jWjYMOA8Hsf7YF`i31NLw7DCcsMX%Z#uX+tyk zcI{d1G??=))AwL zYpb6gwK#M8pznc=TT0ibDzDgZr7StkQU$MdQ(lay{#Fq^S&fl|fH*Va3zH21jL%JM z+f#>~zJtCi7IskQRIc+bJm)FWesPMZ;gX@2SI&rh)^0;IdmUh@M5XvFGTn@hfuI|B_gs^<3QRGIDU#S|kz2d86&r!h-5R}8nU>Z~| z=Q7}Z3A*h5gibQGu85*>Nqy*p$4U5m@ao6-2mCov8=W(v`n@rURh%=(zt!1Iy?IcO z-xD!LOK3GAbu*VzxwhLAe~PXm!o0+7=7jb`x=XUX5_-&d)q7N&<7Pe|X~`8Twjr=HI=I))Wf zi81F-5iOYmNU1!{phnBTgO8~}*MWUd8Kn7^sG13nO5LNiBVA;g3dd$m(zUa8uE4(Vz{%ruj~!f{x<>@`eIk~gd&8~cungL=5*q7uy6#C%Ye;?jePS) z=0u5Wd>V~>1V>-bU*Mjw_->-^Tmb~!-c59UHU(_+;1(((ahS^Z4t@N&-Or9@*65`t zYQv|8GyiKeaA&4se1yy$QMq#q>}WENer+lX^S6EK84_N~30gm%`5o}p=Z({Bv)p#J z46%##H)lHjT@Z;8p1tEjrEz^-Se8wV}%-2 z!zR4U|GAM_M8Z*yT9LU$AjQkN(cJfg+u2gFw|Nf%tp4#gr!B83lhN`4q&Dh7IgAzc zrI17l=`MnK^6MC~OdU}_8!||U<|^Fkj6bhU!Skcg^l&`+Vbs-SXezS4ghV3On;Z-G z<_?Mb>QS$`<@U{MlSp~6nFVkbH+y}b7G@cbv*9a12)G8u<@UU+=<;bn6ZjG&`>DEM zF@z#`8C}qt+g}3DB^xeQ%Cws^ouSa3gDHZZRg{Dmy4)zjUWlr?)UMX85HZU15Fw<{ z>E_3Bu3`H^Vh?sC0WR=6d*I`&U)!mK+&jbAgU>sgc5Ng5=Kq#H6>~vi6l$w1sL=7U zXYFKzi}Zv`T`$7SW5@~Y^8jF2Azh4h&X16#|rqcp))18DbfLmr-xT|^q`Tv$!F zPGo&e)X`KkA0OxTLLSg~p||y2PseE&&Sza2hMm;mwgDwxa>2H8`{p=$p2lZXh=*nQ zU4D6(>RpDn=s8gQ@_l)OOevlq8n(Xt-6o#uoOdyl?lg6r#7)(0#>ksaYKqOeazA{7 z&tswhyQGvzv-<+eQqa#hbIUA^@11tMH+EEq5MER0TByv9!3z6aW#c7BZ+yKtVpRM) zR&;u3Q^af^vfnlDjv9Ug03xD=w1Q_V{XI_vj9txy%|Y2MAF@{?2_LwHH^1@Bc!ZO; zCLwO==M-;b!M*e|`P^ydChfUxG+ABhj!f&Tu|z0=?D;io&6-QfKsUz>tsK2``{Dj_ zowL!xb>`t)Iyf1bnml7^IPv*yYD`vG^}?}dN}_-zB()x;a23{Lj+;`lBf`t~G*oNU zbGHP&pj2a4sA9C?mcaZ}5A4*Ine|bWH#(8rXd8_@b2vM6wUnLyrz}?Oj3eag4+por zP4#uAk40Ibsj*jY<*)m$aM+p6J6T%z z07SMf@5)tj<7B!tnp&$j9+yKNmI&`^?XMv%I3y_dpG0P=$ZoM4XHo3`>_6QpuD-TGu z_X1+hounM_9!@Ar^5gd&dZgeeo@MX!P6~KGprqhb(#Qt%AxxC6(z6LgK0KO_nEKBr z3^2`z!~8)ZtJ7)rszGm=Ke?Zkz{5)+);%=_wVRWFHjtE;YxpZK6J%A~|KwMGTi|%c z4;&en^2Rv-;5Vr6!;Zs6E$uB{<}Y^1S$!UUKg+0e0SlP=nKhQ+r$J;d(>WxTM-q(! z*Gm6$;F#UPvRdlNIU2VeW!Zf?wDbE)-;Yw~UY{x`<^V|lXG&px!f^|%T$et<%cLbd zI&guIUh3g`0af;<3JmFvJMvBKQOJQQ^3(r;ONDylJ?Z+_B#*K64s2hevA!GZyxTbD zI`u2id^YKr)f4(wP?^p4U5oI_jEdoNqbzK0e7wcXek2ylW~}+BT_-HC+!R zPx8$^enE6=xw9if(WZb9Z_;-?H5q9XPedc$*V^Bh@Ndy4o_OxmgV#gT>ub*BLfD6N zHDo@o<{)>K$~%J-h$j6X9*#mhe1rs8KCmRgte!vrD2zR-JF7%-7WAV0@bbRoq==y= zR{EP2*0b*A-u3PAA5o9s_HxYhPTpuffX=NbeAe8-)$y3myNa$>jLj5M;~wYnvLcxX ze518XT+PuL&!D&jUG)C5oh+eO#dBHN%P4v~B&?jgM&^-ZSeW#1`{8X@%*SwuROlxK z2TRyQ)k<6Ps4@Nhh#>Yg#x5szTFrz2wGon5V3ky3TQ$^eLtXlaTe^04Zbpr^0|ndT ziRnxq#~yeJM@E3srB+nq%wMhMSh}YY;VKE`i?Thh1a~`Mvqk3K_7Q4=vFM6QXWkwi zN|GB-A4zVb^xCVjP18@*_*!qKlBGbw>KgjS`tbM0*(g(_kqbuFz>u;)BoRmgd60>t zsqAWQTY6A7@6q&y=NN&nrr3Z@;fm~lE4fQy_1{5PD}ZW<0PD`0T)11DSO!|9VF7S6 z+dak|Y&dxi6GzcJn>P!>*FZH5A`D|M7p#3OIS_Y_`8!uhD3U`M*G{qit&xs zr@B-ZBYP7E0{vnf-T2K!;oc{P>3LGK5 zFK^z2N)VV>a)nSB#+ULTj00%hcUx{+4bmpoMA5Oa?kw5n`5MoC9VwQ0c7Md5TX*-N zOz6min>&9-qbymWt1zq!WxK?W43G4Nm2|A4*AUrot^Dfd!WE;-{`rJ06X+j=nmV8G zRjmIp9hJ;Wh{aZ=hG=Li6=3B^{HYVNlX-`696FIHf${WwVZbgj{%K$Ly0DE&D7m@J z{=SD7Ct~5XUeLSLxQy($97lUcwf*2D7hdY?PYem<1#4-@S268vZewaHy;<_(h78@S zF#V6Vu^f80rC4>$@Fn(Ar}44wvc!##S_|55XtR4G6HI6I@mLBCHSTia#b52e{T%uP zde_g8wHfT_VK4>cLorDpG*^-p>AEJmIDAA6r6zmVNbW=$C2TEH`(U}+zp@d8jnD9@ zF`c$SGfLIbne& z>5FBps~N{w?f16)<_%CDv1KIC0(Z5<+ig* zpY;8Qv`f55%)+z>sgw~^!`GdH9%j=!a!DhOLhjO>YIoc-Fce1_U1R9W%(tR``3P17 zXH#VIw~^i>{Wax|>Vl+>tls(PxMP@mbuU<*iHz56#~FA5IJ=hcbE)CMF!OUfiaw%iWsMfdQYh>rtpLH|Mi@yXZlX`P>XK9aBlK%3YzqcO=0Qu@kpUJ zBFLmYJC|+&##OIC>FwvM@AOlfd=TvR&gAVL_{7u76*Wt(TmXUuxx!OdVBybCNkjE@a3bnLe zrXS!XM5)D!gwP|b9-YYr3dD1OsS4O~v3!1lFx(_~&3UDhKBZ5*TX#rgSPt#9K?}l% zaLPZQWJ-Fl-{m=%zgx(pSL2HWFhh&oRol(+&P^~^2#Dawhc+mPYlt7vG#_wDU585` z*q~d3TBj79cht{X1;3@BLX>~9YrM`oSYAK{)0wDVny^8OKF|EO0{GkZ6$ubc!J^U~ z>a@RrhvMu1X0Xx?OukMEcC7xk!jdjfk7BXlN0_=te(W=AtGI1yxf1_-+51ahIZAPM zy6Ue3j_c2UkK~T5wMp}&ViN~;g&e=hR_@f< zgRgDQu|>_R@K4{JD9@+?S8jtcfMXJmYOT=ShcSJvvzS@BfLhf%%9CTrY}6t5-@pX+wHMmq3ib3VTLcDp@Jl9C}G)n(|I2v zhrdq9bUevYh@@D}jDl}gTSksV$c!q`tgf!cxcK-2GM z&TJJ8Ag`k7*HE)ExwkC$Dm|9c{NS*Yj93n^Fs(M4J~z9Dp@@oWoan&l()+P#1(!bJ zX|6~gQ;jPEAmY3!J~|GQO({*dCnp@&y(@)Nt`=76(*?1{2a?q0=vg)ve9B)gerz6f zrJA?<8C~y>P%96C)r2MS9S7BlTQtV&Qwqs6Y+fZ%gw}4p^jui(j3G=B@AbF`L#_Up zVoO-+gZE0}Ae%$(6N(#(=9ZE4H2QHgaH{pAF6c~{o<*M;xfi!r!m+O=9_)|xQE+!oaQ8KO(9a=kzq8omaVgJb+x00O^Z0CmY zWNzX&`>l5Ryq`?$GS7~o=KUwkXJ_#gSlXki5wv!2&O0?g>&+LA>kWaL`t`Hg=zr>Q z@?=6Z1s#*oSXDp+{~i*b1@QNR70F{{y}iBdGUYQELq0k(_HKiF%n3xmCc^4u>*rMy zcM+vBIAF+m-?!|H+6~_m_jS<0n>W=)+b0p=6{6JtS*+F>cSrT?j7&rz&6(|VETd4U zqrwmh!eHI-JAV$}kv6ayBF?BC zrN1rSi9u5Ix%n0dWHO3y?c5|8;T&Bj!8fkph|5Nnqn5PP4`tgKR66$X1wq{hk*40 zyBK>4-U_{GXYi04Yhk^bF{=nym0xxd>P>DcsnG)k&fhNOw4+S@(^Su_%PWCN)!txg zbfyjC{^s+w$Ap@D6|B@#uo-kwn_ci0YxclaR8fjJypxrJ1dK0?$z6_tdT-+iE9=(y zu@)%RQV|Y=E6uje)uHku63Z%=UrD?UPOQ94A0xqMcw~{4%hJyEc zbacKht*E&fGt;3S=e`sgdZSFOWefCOh_S)4^iF!=o?bQftIhg77Z{9c4PJ6N%JN0s zA$TmBb42_uj?R54HJ(XSNIcQ?T*Bf2X1dN=XA95oRYg};AoU^GQ zAUz0bJ07s0BW@uU5jX+6dZyA4_zu~>mcE&!nvIuWGVXUAph>`0i(H=umeC(_W83;P zUoW0QRwnI4-hEDS;uyOZf-32MCQo=>kP!r{dU+$)Nc}$lDaat%`T#Jf2z`6@5ur+Y zsO}dpIQ2wl?8@r>15=0!7!bHOx&(-a4beB^fjWVg9r-w{Jw9c^F%cXI20E%Iv?9r7 zjQgyHx^wGYTRdT3eg^wb&+mHxdliQhZ%zvQ+qi3w0ff>uTBaUFliv}&R48=QCZgU+ zSjXrMljBdFG_ITk3Mjs-e+obLubsq|u^Zbyp z6cfEMS9iGe=)2`i#@UP=C&n#9Uc*M4_>IHjZY;sZH`(P#qZcga$&k~@flM;Jn3u{?;?&} zsxiCzjfu%Z9#5kV0Qki1Lm3;OkeSS9=<7Se-l*)cxzTL3To3s}1jNfZtJkw9ek==q zH=gNyI&p1l|7pO17$jm!*7D>IK|wJCa9X*RxOKG<7(E!Iflx zaY`UVd#c2U0`jMLKi!>#MEjkMCpu>E%r|CtMms!vmI`AV#I{wJ#t3up8pQCNC^Piu zy5WFq6;Q2;z-}@2A?EA)6L9D93ZLdy&%jiFj8wxWGnb}(-`&i`y+OOj zGs6|BpvA!D1VxZ-s~~*)0XD9qV+rsdu}n_IQE~*h8wtO;!%}$(=91DC=6^o+-XjqH zbQrx42$_Ch4Pb88`~#}3J#jnA+PB9(jH0*J7Hw*whpie;fGOeF+=U0)GQ$VX04{%t zf5`E);Qa^q4Vmze?1W66YJUx5>s;gxkHXhgYIfj%2)JEb33dsp7kTCSo=6UZ;=r`9 zrC^V7Z$7E|QjQ^^pq|H7 z#-=7Ae5rNT9+HhYw1+W!gML!YrQU!fz-E0($Bfiv)E*PQjw1c0KM_PEuXSs^Kpwmb zs5h<}6X*zwaE)xWmQ-s82g(~oC|8bWg(NSaE=>xg$Ep>|oNkU&ZeH94@DN6ab#T;~ zcASbDWgCNHR(pV#OsGd;?2r{kU+B~7MB=xrY5TD7){xb@49$7<&A z)sTFtU8(6^57xwS{vb#IZ(-}s)x@Uciy0KtfIiidF6X=_Ld|Wq@|Ojupdk+vVtg4M z#nxbM>6|`Y)XR#YQskh!6e0>vpT~mKrA?HLIo20!*!#-=Q#4$BS6apDjxdP9P2QI) z!xZkeT<_oK(kQ;*PnF1V4S`$*zQd?{83~-=y#X)#{;B)Akv>iMe3ZpZhuur3$e{Mg4(Xxf!iQ1W zd$(Ul=R;?MNHgRH*m1PBn8rWXgK4Pj^I>IaJ^sRU9XvBAu?R5R)Fxrq!*s4jticVo zfBi@J>R{jUZnY*1_)uqHBbmgrEw*-4fq7N1HSeJeBBP-_fBC&qP!8FtqtgLr z{R|QDAUc zVZkjgZ&z?9uI_sCa*x7yyb8Mm%Z$hojo7a{nne|1;;gwRwUO+1xxH{M>2GPdoc!w3ip9 zP4=c5Q!!|(FN!K7_u=0GYQv7u=jUP%CAD4dtJEg8!!N!tHU|T92t-k zba|I%S?B}SZzgiv&r|g-tob958l4G+lua>{K%9ZY|5wP^I#m-9O`sC>`ur2_ad=WV zZeaW&8?1_;9-Hzx6B$w6WWD43zuoauiP_Zhx^zv!=d*tu-iZfW z7n0o|H*E^n=QG^#K^5~L*DD2qxzZI~JW?Y5_s29ZSrq}8{&|BueNkr8jZ-yb7?i3x zcSoQ8SZpmft1#TH^w&#Nuwi^^mPTL|lP~!q$3R2U?Mx=AKlc&hMt?mKk7Kkp%PbbQ zL*b-sPZID(C!E)ciJO~B(**Y8nd2?FK1};b9~(g8iT=aIeY*1K8179MD42Jsi|}`- zk?q>3)F^`8Gwvy5!0b_v6E`YT{8**Z?YBs1FD3L@mm4i3!k}x6lL^J4lt@#_@p5s8 zZE4}?8t}BX^iy~Z*VOJgx}ePH#UsyhUHrW^&sHeRuz=B0TY?qcd({p9WCd`U>M(cd zhtma{t~U0Twn5*LyY7T)Ix~_m8tDpDdWQk}325GBy@vv8EU7M7NOOQV@Pao%<=d$x zumC+c>Ur1;rB|t44|UjYu+8oFPT2+W1gA$AQNzUzAw>;(me3peD(@4B&$^9*A>n zxuCKD>Vd9H@*m$U-nm!93b1GxsT(YBq=nS}T^nLE^wCRS@s@$)1(tcQC9FATuM zERS$IeFrlfeU)p*ejmB>;o%P`FD3NpRr9+~u6tRbUW<94V401XN7)cDr0>0A2KzX` z$OGHiflWb~blFut7ic`mFRbn_R4S1ck1I8m9NkWEMVU2sU(HX^^~nH_J%VN`itHq| zv8o8|*Vw_)AK}BN(5|KRnnSuA8H~KsyE)Sj6yzvzncV`5O!0OBaa8RWf@MOy;wfB9 z4JsH8-x+LYqq8`9YNAx6BJi-$O7}$fKWfQ*@V@*$JhaM4(7i65 zr{QY=wPevpn*3Hb^PAf~PoA6Ey*Kj8##zZ%Z+jfl*+AkNC)h>uc^w+=kNdjSK@oXB-u7h857>nD9h8XON*5F>5jgzUT^iX%b zhMFExjTG(yG8IY>RJ`^ox88z77X)rJ&?RDWWm2hq|IVu>u0`aKJ$-;g-=+5hGOIgB z*14%gSgB2s+ryYZG3tp(ttXO~g*EgA`YBT(N^_yQiLxpx6A>bbYGZh38mA8ufak=# zf&vW5JT+OtwMP8&@b~neujubTe%7iO!^cq9MUIX9s zmL8$9JrM~}W~lIRX)jVbqC#mDq1+;;xhnVkLyel6w*2lU~0?xiWp2OdZJ}?0SV5qKOTe40X%J{2n;hR%Y-JvhnSaheYJ{H_V3HgN?;o(OM=bwhUJa&6m9RAuAkM}E!5EDw89`D zsrxF)SQR>EKx>Gu-~2Dlw}}iDo&+LjAE`AVkTzfUVt-xnA}oIQIQP8L~5VWfYo z)}4)ARZQIzJ{jljI2nZyia9WNlmKB8UV^1L9rBEtyXF49p>8hE zB<*ZW2Q3N7u>HA4a@8pt$;B{e_q~T)JiEc=N0*%RUwHN+-0`6dy((Xf1?cWZJrX5# zuqfbw(UU-0^;K6HjZ#Z{PgvsL8GgW_WT!Cd?<+8wwTSOTY4KDbKU8mBX7=N}1L;M5 z?2So4zNfCuinLU{=%E>?CGLYoi7lYs#brUIsrU-HhxW?+0(o?OZqv5n00d7lx49B! z6Zv+L_UxHVTP?&t#Kjli`utvV*oSzIe=MUc*tdx|6GE)@T#Mo`yj@9FV?-Y~KO$HA zOZgn4muM>=eYr>JkE*p=BKG4b+2&^d`yb9ccxkgBuHKkc{{8egf`Y?fbVkLOX{e`CtaImFFsE`d-_}$n2}H@IilCRH+Pd_ z3pZ6NMT;RTm*F-xY=U%8SjQW_J?ob<-3zsA+QI7HC@+XZa=}cQAOePZEQh+dq$NNF zJ7$n+KgXG&P@`^A>s`USEi60Sh9PG}1eXNZt%|bm47^+7($SbX z8hFRb2YH~@(MQE!_4i1Pj>=vZ_1*RkQM#s|rD@J;P^*5NTbMc=>rLF4_`^0S%CmbQ z;lE@rQkCHyRd%^gpXz0Y=w+!*O8aQYfl_UE6kqDlR(kZ+uv*#uhJJoH2!Ao6Z}}E1 z&o+q44u}XA>-<=CeJ$T6R$OOQ%g8ZRJd-bU(YO%?$^X74wELKnslxE)5`8#fG_+)F zs954zk+3AHL}L<%$3g5AoDgLy`|!Q#mRF_gopM?`s=2Qs}~ zMS0JM&c?uMEDm4EExB0|CnDy12E)SPkJR*6#5vf`ERPRp1x*T}IxObcmUl06<~)#z z&K<*jucY)nuBOSD8J*Z1CTlqi zgW?%2h^B%!cq=fpZ?x9*$=}z78A-(W?lYxgue3=E8Y_~!3#b)p5PTF}7euemG zD?y%OSq`en>W~g?L+R{&C$sN2w-c}Hg0Sfczm(Nybc@d^Po)-$8Q4zkW=Yix+(1RS zGoajVWnAz=OVk|Ga{_uWIg^|;@|-B;mnQ|VbT&E~b_vTf^vE$^s?7Hj_HpsgV~1{@ znm#>x?}MTEe0A{gJ^i_o>$T9z4GV~u=xKFgvMJ^vVg6>jQW&ztJKmAb!xq6)cNn`6d68XfV}mdqG;<>;OF@PD z|FQKZU`?G{+ps;h(mFt`#aYtUTC9j@9Z(rkwaQTnX;la^rAQS4Aw@vOWVc13;*eJ@ zq(vd91(YcwLu5)+CY3^f2vH)0K?orPGLv!lUrE5e-~WBrxsJ6o2|If~&$HIL?|a?r zks$mT5B%{Y$?_$Wh!YsbcQ_Qt3(RTMGGmEBK?4wjPl?q=mQf+|f8hS-3*zqZ%P>@3KdV4T z@GUE!u19dHA~UYg8ouhMIbV@kU754+I5mgR<=m^7G)~A9|BWX+H$hK2V&Hp5B?DU;+NuMi0 z7ivl(icPHNpTfaJE&4NdByHHRzHSOoncP0x*gJhOTfbft#a0>#zf#McF0~+f`J?n( zXM;e_)dv1HZPaOM4_Snn~_48f>m!3J32z@h)r5q2L6-T`l7n}GKBf0Si1UF-LGdR}N z8c@Spn3>RjiFM0!IayAj9VV*VG+KY}i38XR%4WD+ zqmbE{N_U!?kG2A&5um0CydlT=7E3@Y*M4oLyNOtu98OyLWJg@k0IS3)0Wwr8M>NUc zfMt((4a#=v;h#Mz`$6^@*!%Hlj>zyexHm;cadwLJEXxc+JA&&(nycAY*QMzS%igw0RVk}p2zaxNE%BPzt_qUTYxVtzrk0jA9w}V||Qc67< zH;x(9&yDm^VgKnyWA#DO8;#@*)y7z{i42<$AZ?!hCLWuszV4Mtcpb*r!9M}TAXbn^ ziX@a!-6xRELubexhx&HKfM)mV%P{QHIxiJrqG;7rg`q%$Y{c&wpq;D9ueEMw3>Arz z!jtGQ7BT?VvrB!E!`&9f8)D#t$0x$BT{@bP`5YxVA%0u?w^34eA1zCe;3A@l;qc24 z*+M;&g%&E`kF~dIi1K@BG@7!Zjn%kb4n$O^Gu!z)uHZ_F-95co`7FAElIv_Y_;CAg z%)X={r|cd50yMz&6~N(aYosy9s;gLx40)A?;kN>_Gte@ES`QuHe5S^GoH-?u>FUO< zm1oEJ!jU&oHgiFJY&YbDhy%O@hjsY52P=dv1=iXvgY`Rfx>aI+kj}9eDwz=x`h^RY zJ*c1~OQNEje3)Bk7%c@xCpA_vOVuRMFRWqc*sFBjyft5lu2I69y8ij`DhX zh^_3h;epMZqKZT&wTz>-1>i|}u_&nzFWwzV(HiAeUZgGgYTH5Tc@Q66z^B<)uGuH1 z38{$;pa-!u>(No4rtgy;=x9Wm@|Nn=Cfs^@Vm4U4zld9TpmxAYxkTvA2k=isCV1x8 z@7p6m(zaj1%}aw=6uw(?+H*t9j^8WfAuoyxw<&-Bj3s_iyXmR&gG#TNhe>3tV0r1KoWm_&2E-T#1~n{{=<5o5 zOEOsN45xoWoz$#S-~9qZ&5!_)2oaLQHxL~=kW<*z@f079!fC~13=fvOL!}PM$=+~Z z8gCPMYTCDm$I@GjC2o1_e7L4Fv?}uf_G@QH>Dqk~^5p`Ur_MIy-Gv0@E{H46+E}Zl z!Nu-xU{(B;2>7uEzjRfaL^=6-nv2lzS|1uQVwuOX5q*AhnRNP4Cx{w7bGVQ>F`k3e zFqzDJQIZV4TVagdR0F&nR{CS5?T)T1y#bs0(+;e9T}$mV_$#eaTtAl@M$%ODE4E8} zrU}BTvs6S-Nm`t%-`nYYWFz#po_wftP?N2PkN?CGgT8sq&cvvE9+m#O-t*UR_WU;8 z#g`E<9>QSPs*f0OT2;H?g5oX~7=}PX*|lc-i4q~OwUvT1SFrqa@9v2Ftb`a>3#nIs z(O_-SpWu%I6(cT=Kn0&2bQLE^dd{2ME6{GQ=jsy>I983{y7NF$;}CaaR)u$0)w8o5 zQt7dkxvGn}yv(#4kd)DW-j+b_dl%Z>tWo40xAVkpMNWS3Z*HG37@dk*tfIB3W_q(U zJ3QQ$5y(&)HZDIsKkpptGfo9QpvHzcgzN3HTGXOiW2xmGezEfbczg=<@t{@dsdFXr zuKSnk_Ft*qUfI^7ukM2@MXT(D_p3iIrP+FMkcJ0__nxW_Biz3={6<=3L3)0-1KNP? ztP zTpSvjMqSCA5OtKEMLnbrJQb2UI%K6Wl7iUg`r<2fKPp3y(d}uFPfe!_liVh*;R8Nm z2uIXw=B4QVbjxlT)+{8m(gp)><+lm;qqW9#Axv9`vrjt1^Xlq#N-h~CbzfVbZ%*lYY0uKSjFXtZPu_u1;yybv89s~^@& zTrk2F+nCbj$_*wz=QxNZXG%HdEdx`=!)crP&OVh z;tyj#y-?`jqJBZH=DgF6(};Opw?6*>0y%bjOu%#qBNZrp9czHRe>Lrhru)K$;p;qB z&qe0cl#A1**2(<`UB@AT5PPknj~KqRNyQMUW9ikd1@gRi-<(NIj{`(5=9**QHitfp zpx%hjIlICq%RV)U8|ve-x4GJ)4&Ae0(y>`NPD;Ot0oyRsb~MM#ApOfXfsDnR?HE7j z)NcETOB&cqkS^#i+g^urf}Sz~K@=Y00 zHbkG`A(}H4wRLFMi?LDUx$%OAN0(#n`cdf-h(k?F1#+5j3VZ6;hGs`>CgetOL;uWK zK8~N>j6CszZY-@HJm{S8u4@;p5zFBu^y^?-{qlh81|5Cew&bg?_Y8+)<{M~FX<~57 zl`A>0--oKUQ+Vz}{Hcjgq-)wLS#T)l@X^Bf6WT1bdfd`UNzHKSuo7+57uN zr&#-1r?6oT7!Q=PqdGb|0y>D;+ehTdp3*fVeT~yxey9$wH%J3$6WdgOF%Ln$b9G|m zrPV$AoE&fQbaI80)%@1d_+B7$u(Byb_+2RjfRDjdUE-BuO!_4!tTRcN z&+mo|=Xkj8+d~B*t;}9XT`Xb7=Fr)U++NL6trtU;2CE$ItZ%W$a2v5POGOC|&hr=Y z!|IHSA9yIg<49a2p@Btx6CYo^o7?w5!GR;kVV@#paO&r&;tBhyy5LgM4K!JdWfPy* zH+V=!E(^4m?L4ZXI-|PRvebWo#1V5onS3JfXm__#JesuH@#@}EA&K9UO0G8)BGviUOiJ&tAif=xxAV1r= z*2eq9_65BB9$uNeMQp9}7Wz(JMpINY*KrO5k{bNz4vN&GWu=Ru5zM3SA$fr$_%7Xn z{T`Vu?{)RE>>XH2jZN4s!>yV_JQvCj5~SH`xoD-5rT;u}gsqCD>&c|jsRCg-n4v6% zMVnj#sv_D1UkHg?AUsab{)Vnv>8_(&FpG+-;Mk1pSy?EF^A<+bi8VGfdh%(h4J&U< zYms*=6hKa>Hv2zXDnXy?!~8-FNUzU-9e z)PqpPt78|(f2!=G_MsuL0ag?XBzI|EOqdg$QP0q)+HacLY~=Ek3-Rv^(0WDBuJtdn zf>m~bMAtJ(6m5(+FxBr@)y4jlzz9D%hxl-$i9Gq-g|1H4o+4e%M_*+Xq*sK;+bh{H z2^?BrbM=Yy*^)T1bt!WCb!gyf?xu)>ZU-y}{s;1U?}s;2S3b+*!w0QX+Z?dn{FC<; zRn?90Vij6UdOT>g3Rg^`p|-Tp7dwrF#+c1obz7mD=+4l^>62iMv-xnT*z@GGV=bcO zqP*@5Rta|SN?p~Fh7r%Ai%fcmA=GdhpCxMTuLw6vc|&=-5_ zPE0e5Z6GD~j)$441Yn1~Ez5iR{Seiy3)Jhn99r6+D%kEEI<7d3_7V&S%KH$I1gr5q zHlG`^Vw_s(;ALOIf^vv{Dxt@aU z!JgEKcR08b zpNP{rj9^3^EjK_vo z*4|qbkuM0|_MOMFN$iF}`sq5x4kMol`4GM+^+sSIA;0C>aqkE20pPPV@bi_#IA{sd z9$gJ8HXW1;UG*vLn>K#)KU?+n*uHztw0_8oz$}3PFoB(ANWsu4bvPFTb`QEOVM?gAlI9A{SyC1ul0eX(&DEWfCt{usYZ6t4^}7te z3g0!%fAHMbbGbKNyM6{Kn1;Vjcs<BZJb^F4!ZnDZ1P1k9$MP;+u#& zwsm&#G4`F-H#ip-gChgSJK+%-r1q=-NKC900k4jG`Y4sqpTHjkuI5qG*$>t7#-DmM zABI!0A#k{ef_v*X0aDxxUX5r#w`ne^j{3K@?hwZxWbgci8!@XssA4qzSaa^&t&{ z1_upF$V++|)+qx5$KW6TdtuT&himS*2Cc&BvPF|`eu_@FV4PkY-{1!`yEybxS9ZY8 ztosx7_UZ_DHopWnt9I)*2>yzz?k3*Fp?#Fi`D`W$dw%)XM>{MVo(GEz7q{PUr*=cU$SP~kF%g%AGT_&^u9 zLzqNriAqRFT9kIRG+1mcv1krR@KRZF!fVEN9h?iwIHPl-%if$u_MKiKL0Krv!$Nz|)?*W;cFp`Du@xCA++H2)AV+ zzob7xk!#SVK*C7)16EC}OJK*B)1q6_VAwdRBx(>S>PU8kCO9J$^mA2=4{=yk25d^> zl`mbp>qw|uM)-+5W+JKo+~9qe3a)@7cl{wv=Pg4A_RIsOg7PzV9(ir@8EIe3`D8m4 zO$~2;dVC5^h7l~;;a%~p!XNVJuae;N2%jfZca`_@C1Zw%63j%p>$|6V&<#vxl@yb{ zjiumWD_>D2TIcJh)vt8l$Lqe8+T6+uD}U{0w0Eb};$U|qlRds1ih}cWYd7ykKrKN} z3D(&zxbpb!m#=uWIL{l}M2tl^e@TdR-L&2#r20h(`U-eUY8))7VPW8Kf;6S!dc31< zLrX(ehojLs7m2+n?Poncw_OMs?2(( zXpB6OjCsKXvaz#5w~D`5$gugO8g12`=;H(4uO+=l+A`!3{Al-;lsCA-P<}|w8Z3A` zrBN3ieKM4N^2T8qZ-fko&N(E4qb<7CiW#Y-j&$g!)K{h8cpA}NhvCtrxYzwiYu-;V zAPQ2KrV@(=`4~;;xQrlGV+9Rx`tL0L_P{~g^F(SuVi@zJPuL=-5|?Fnrp9AeTBYjO zNTwUE^N~CLBFvAF=Kxo4t#Q!pkLRHuVOQYZcy5Xi16?T&>^YbcQ<9ViswV^D?NYqM zL3FiJUqZg9MWST&qj>^%jsz07u4>?IcgNT59ukg_=4+t;B1@bbEG=Osf$Q;P_^wqWclrtu)`WqEGH z*u)n#1^&m-xD0y=ID3+B41LLOFpXDof!7V}So29AD^;U7gsU}Jrab9T7s@A4L`8k2 zMe>fbg~tI+pMRF|qk=Qpp{IXXpM%o6^a~Se zp?w4j^+I?hLM>n%ngEf~Hq(?T?_YLdzW2L1GNoh*mN)sNVj$G9gFL5dot?cQb($XY zxy^7c4@Qr{?CpOIUP_~HHz%9Cwb2-u_ylpzEam0w&-Qf$$f~Ki#I2VIYnSq!XoB#1PT~@%HkMTPVHzrKZcSZ%X5sqLysM+V4V4jNEJNI)t<=g=4#ok5k#c650f8pyv|%7<%JRqH5=#{W_`I~jT6#lk zUcKwEG#n)R2b%boV_SFD8O?l*J59u;j^nTQwF+<~^eVIpixopP8J?|^Ctrq<1J3Gd ztszX-(d3tc^}3U=qPPdqJ4VfsVyNDd+zA2m&f>i;OLGF42CLg-n`D<1gN+WJ*?%8G zGT~vXMQ9CqB!_&pYtqg-0FxtjAR2|KKlIJn($xn(71Nal2NCgRBZsdgbQvr$K6TP% zS)b7S!SfUtTMP}?s>Ndr^`tnGAtTRFaM&m`iV~M4;D)&>s8CoFp|Jd#sF|V$K zS0fh4ZB|W}>O^WXh9lcGW&&)vb$X22wlrRTl)>!&p}I<9xDR0t2e%CPw*z(sivwOw zLe3CK>v?@;%V?-Qu~Mm{bWtol@=krlQ%3{b!RWOI`4I=*qa0f~-N0Y0mi6>6if%++ z)sWbpe1R4(NXXe8&#a9YDWQU)N9Ole)|Bi<*RVcfQU+Obl9$|nh&C{*P;SkT+^ZYh z6&pY@u}sPk@5?(S8OH>1?U{T)Q(Utl9;r$s5Apc<^c82ju@0iAI)}pM%HoU)umZ4w zx@=Lvdm|++G?%ufCetIwV+VB4&<*i-*fQ)80Y1Ya4iQN8QGqZ#Ig}oIy5dkD{Y}t? zN~iS-lx1&q*ZH7?KS)(MUj6Iw<$V)~OsIh|g-m=0dEQte@lqt9d`_#vFCtaQE#Cl& z8lue3N2MEGHvnQE!IKku3TVHT zOLC>Fncv8z(Jdo^smttDH7X%15Io%8OQZ89b#>_y%3K(U%3$h4z`z(283b-|HJq@h8?mg^f;7Y3?bp0`!v5NXV5iXun;Wkn8SkdN?Vc*6Q{X? z@iEjvmUxYDe>`XHLmd_*4I*AIGfw?jy7rLNi$V)lg=g>6L)ChlI`f<-{~OPe%V*LcW3hP3fM5(ZG9m>g~o3!&GU; z^{wEw6dulMqusKZ3*;et(HM(ICNWg!>6oxuI7^^njK^MP?ue(j^+aw=hB;D(axI+Z zuHB>f$Kn%}bRy&}k-ZMYW#6oUVt2Hm| zBh~a=R~vv`@IUJps}NV7c$s&wn2JzE>C&=~Z{Kh2E05%neKEw;20OnI#`ORL_OEy} zPukKLcWwMbB+}oIGcE@R7WEojsVWm3jKU6Jty*QJ>C5*Yw6n8U_(o<|oZOlG*<-q~ zHEAJjA3NU&To`@AC^K2fJ%IGlZ)7cH(5T!3G9ttnA!%>$-zN;w#p~-X)I-erD{BlL zPFZ}85T~~iO}@N!rwxUymOv2bVXO$?GW;Q&Wjy}zQn~s{bMGi0g>cH2Lq5W34*Ces zA-Ny}wC#W5-IrBmB@l2hXJjR@nUH#9ze+g3DJg+$YLMWd_mvh;p8;}nEK#6$7XyTK z1)Gr^=yzS`R;ah!?5z1L*1@k&?1#jJ%;s ztarN@etECRuyrB@s=qw)<;>26Gn{b02QtkB8EK0DMiSnK(#Q1_Or2!)R-}cZQNjGG z78sbq`J!`#kxB-21y-Ov$*i?+++C%2u@cmYcSq#i2JgBN&2kgd5W?-YA>+VI+M$iwwY-KS2mQD>q5f&UQBrI6f-p6Y&fQDlYgpqh1}2TGAFc9^6aiIQGtcG& zVnqLqvsP>Qdh}=l7M>CoEoA|BwQ{N526b`^WfE~fD}$i+ZyZFq#>C^~kH#cuf6Orz z5+!`s_;)X@_du<+xmdrcFx$(1dTjc-aW`XHA^7Y`T@wxq-a+=ZBWfnciCHs}{#J=> zb;PoSD5?&;PzU=0gR*J^e|hPBEzqJP3Pq?xp-2)Mhldd)H-fHDsKnr+((lpD%7!`w zh$;i+NTao^ z8QBZo%mP`s&GsD6M18XcICsLNK>85CI`g`p@wj;Ze_4;H?r{U!rN*W?~Et=7k+C?}5 z7a$hae=eJSMg%SxQ?v8?39}x8vs$5fGg6{Vqm$Nmk@&n+7^%ko6)8t((xH^{g|rYB zaqaH6a-mu45$>*WBOWySaWmOy3IiJ4zG^WMbs{ssbmp#IC=vJMvA46Si#>71w(#vY zpnr||r&C`b;hra33Lm`wKvA;95AuqFBDNSK7rR|>aDHbySGDF!+F-qt7%LZ^bOJg! zqdV;hYlng4%obouHQ+&NLm0yTykobL&a2o}0`Pbb)IKgn0)yiKk+*&7{x}EFJsv#f zR>4aErnUDQx$!ub7G-*mk>>ejJy{SKK=3m7lnoQp`d4?vuwE08zrdZCt!aX`sbssP zArH^~{ua~)eFYy>64*?jczsk=+EYo@*pz-&_#|i-O^MVQ=17ponp<5ckK|H%0{}S= zUw;mkjK-QaT`+FfF`E24-LHnsgKDg+<@PKWa zP@&oeBGiIbU7~2>h*d=>taJf@POwKa64IIuo{qifhkRiL&y~YTZ`g8c1utWr@PuYl z=43+k#z4$L#}l0l4!WH1j@oKpsyo8iyWQ?09s0G{c_eo3*j6aUPMkud_KX@M4!|<* z7fx5*M)sB~i;ltA-upp9mKc8|&@Y+6Aa1=c;h&GK4@CKgB%>K%u|eiycq=RUn(ZCQ zN7bxB!g=y)-6BeFtiy*$Yc>}ii6@^bn3APuE`H`pW(Kf}8U|eM)Jzp*)KLqrYB0mB z%UOc-($X*fnR5`z*3XzBFY3Iawy~HxW2WX>IsJ!EbIRqCjN~^Ro zLi{zff5Dphqoc#d_)`#O_)tPWpX?^#yUVAFBUoK54*40}r%$n6b(g&D#;L4q`gC%Q zO?WO3J~zOFGQVwzzLL+=to#CG(Q0{%{-G64Vy#Fm8_@MR?vc6nsj?4@!!ZZrhk?su zM)U7UTIim95aHmyLcI*h6!`IPghl$OkXSRCko2sXeTTsXie5v8{As1_xt!gCT?$I1oA=A zN?&9FK5n4E1P3_Fh0nX~3r0AMzLyXfDp|sMOfLaeP5_E+)mO%x39DrGSrS1-xb#!_ z>CCv~7pGi5zj2^OJ^(bCfM2l2jdF9yR-@E~>@jRX z_a`vvLkFieu@E?RH6O3ub=GgaZCl|OO!k8S*q`I9JUvLXCQd-?jV#=EaRFDckx z5L~nfa9vko>Qp-{Izp4ri+nk2E|eScEQMr=>LE@Yfz6Z2c15j%r~5bDUhaPQBPRtT zkPXeciq)_Eqim5tgC--NLb&`(=Z~g9Y!Pwyv=PD+kU#t4y=C!qvEU|hrx#QqaHsv! z4UTH}9o0ZkFW?CS-SqCTg@u7%1bKhlQx)jz%M+_lC!@icyX#%wuu80Kr6aUPVj2Vg zA;AZrB?gN5YzHF(XaGz5`I7?K}?v>Mk%l-QbbykUE}tPF5ahDL*W)v;u_>;QXKur zx~3Kn(snsodlvidkbmYuSDzS?Q!+YLdrQFmai0V0&E}opefn9@rFX=Ja7FU={iwT^ zfK>;}>YUf?;EbU|jjN-8Cp*76rjjRx7Ti;&gyftlG+Ja_Kv}ZyYpHD_EoO?ep}Faz z6c#{Ecc&PsS0j&{AMDpP=v9T~9}fh&k2t@T^QE;GSCi=di7p3d67=8kPl z*xKeW34Lae+-HslZf{Qd zQ~<5z!v!1;Ls|S*4x3x&0%e<4|F?GimnI*>4~(Ew`6w0j36@~epv#mJHs(_6XXIT_ zu}Rw!+BbdjAsB9N9W&<@YdH<;^0QfcXZq2M5%v43h9uVECVXjh!PSxs<^UjAW@DU` zor;GDc^U_1QfHJ!Juy*Q|H{ z?*?$+JY(bE<5^5&HoM;?JOkbJ3H?N9;7x>F%g8k4j(tZBC-tr{<+AwPNh@!P)X8FW|meC2=R^mt89=fGu5^U4S=uaa$zoO}%R^k!OBupX=Jv+Mmr zDmb6)wT_i$tJ_|oV#%flYDv={ zRR`Z{Tc&!mvH*Q=n3E-&TBL5+r%nn2O+@$&$W=@i=(?t1*B5!Wg(T++W}nR^-3D2F z-DiOZ{n(zFyA~x4v(hV9MB384UetqX7T!P$gOY3T2g3tVdpBV3#`Z0Wytlspn~G6F zn&#qypS8C;t(BBjxFKx$<99J98G0;^oBp!Ay!&J&?4Dts@iWlMs9ie8+d7tq^JVGB zYn^TDt|>X_s!6BI7`Gm@n`m)08qGk@LpF`1DfHnY%!2qYS|(W1Z7E_*7`&flQuElg zM2~6XbR}i-_&-`KscSD!RE49fv~FE|3-klwj>BvcwwKIqAt5J*r=Rle?tlAw>MG2?C4ep_1tj}rt zCaZ|i0@pg2nQCacdrTkIp?AFf8p-GRkuup5I5U;-Uy>b$XuZTHeAYBCm%9nu842@s zjfY=LqPX=;?Dt8}d;GAUCgF?@mVt8B|46;_mR%)nXT5^g6j(Q?3x?XJUpiB;La_$% z6`_A0&AG_-q&fVJ1cbX|YteV)2kPm2?zu-f8W{s6euiyT-B0|SLb2WLEa&gd{5`r@~y_$d2J>$ROw&OFJUntDPI4ie;6qiHfK&tG^>) z;&32UcX3t34n72ADqYUu=)(sd7T&x!RL|qV2nj+pJFTBEWLP0#HM7&0tJO(`{(CH5 zm15cT$+UZc;7r5TOt=&Ycb<)m_lRNayS^BNbM53chqUeXf$zB9P>~MH;gb&;TM;L) z_gNr48ge&O^BP!ECzv2*l{zD76peZQU_Ojby>L^ z*=+1!=}!RY7b*jS#UJ{-^ym3OPDFb_re!45u@)u=4P>8;cWsDPCDGOLnqdqVn!&|E z<+jdWc_#A0(X{sC8xG`Km-abl_BqkMjVB(K+N1=%yHcP}GS)IOeG#VVNPJvlRzTC` z-&Oomm24gs3v0*4PI$)o$LvaGktd&fOJn;{SKxGw!m~Fe;Js5)0P-Q!eF!XaK>(gjW9*>tz|hcZ5d!(tACYhc|U z$A0S7wh1yS_<({%f1alI^pL&1zgh=*K?xulZ^CViQM7~-lQyI_rU$%d2 z!c=D8`MRNGhY&r0%t&QTe8TCZ_hy=udrZ_1cpm50A^|MIqZyx?xv>3yc?)-}B%1o- zJI=1$-1epsYXyw&zkSj39tO|~#_54$MtA zYlm;Tn0=I>qYGNPQP)plwb9j4;$sS%UnlUh)~2P=9cE1u%xL&e#lPfCiV5o|l(fPA zmg)CL{{qG|34=Y(yNVA+B@MZF4++GMG!TLAL<8*Zg1wjgk&X3z9X4$vG89CSgAqNR zk(uWamf07P-!t_F*rLD;kSTRzoK; ztS@RDK4VO-B5+^BF2&0TfM#y(+U{TzR?|d(N$!h|j>HP(dVoWm@f6WATKyxane{C8 zh<_*TLDHE%h32f=t<;9X@Bk*971e+5TR43U)?^z2ZRtKqonE>d+8-c1M18Nkgwca% zj$5Y7m4-c%62(`B8f`OR`VgkAPzEQD^r@sj!p@_Hk)yS$Fi9U)i%|%x$FJqdvhO$5_^R>={1#YFZR=piX%4ifXd8{SCz-D4;MCm5hF#BKy zeeEI({EEqm;u1rgVX<)Vt~*<+(I!(el$72ot;}Jqm@jqEVT_6_CD~fWqL8-|!aEae zV%g|>-0s4pV)cm?UB7$R(&60V4kWYV?s+7rD6QrY?xV)v^HWEDseJa&93a>j zYJ5TX>7-r12e=ap-c+Dh%jn5RnJ@B;Wp)2eaMae|vH9_Z+7-#r0fV&AHQL0aqZ`g5 z633mSYfwZy`Ys&vVXXU87D5Un?kLA zJb95XAq}2;1__pa!a(a^c^(WclBQA6oxXaMbhHBJ9znvowm6o{8Bf(l!RN&(Yr<}91o?Je1JA|irgaL^lem6F*WU6l^y zd?P4urx!9Zu14!^^W-9?G=r+34D!T4j{}Wh4`%M+W_S zo#U2_9Tylju;IKIfAF{43n?s}5FAw~D3dz7TNjQH7S;00!13+npJ%m7I?&L+@9CTai`-S5yc)!JX;2z`5t{s^cT@IFlP*ETEpG!*JO#qe zaU)>P}kjDxc1idTQVSR3{u7$~eJ zvjohYs_kF9ZCQh*s=dv_KU|9KV-1Pggr&*G%J%Sni>U)vWEx6`H5M&*3(x+8+HT;! zGVspw6x)!psZpvBcpxq$1NXz?V=Ote28<^l$1n8!+O1*Fu%i81$RHNw zRcsAr8J{C9>=>+f-yCoYwetU&`oKVQj#9^z%Ex8C&-)AroumKT>$LPz5^}ZK?hBI!Ge!Ip*XE4X+%Mze zdhD$J(py+!M&8hIdbYfuJxBZnxwGvu{|ZzV8q9HW<`4nxry0xndIK3bGuzc{wa@HI ziq2S}q!|6XXOoaw2BBkSc*3EXSDCLvF+102HfQJ|nZ_?<`(G1lmTqV|9b=j)Ve0oW z1>Ic5Pkkih>)X}sUG=lwJ}F7i;57f=@O-Ut=2p)BZtl*vjY}pxV$T0oa)5YawJSVo z^c(uj8R-ap*JkUV-{*KZ8k;3toV?o>6S=+xii!k~#k96ZYvJ9T{$fpUF;><9i}E_I*G^Vzn@{CV zy6jIKyT^YMi>{I@lXPa5*_Bp8dOUzlakZ4%zC!!*d|$&+)i)A7x^)D1z0)r+3*&OJ zC25Tq<7dyJKh*AJtB8`=Y&tDCIG<-c#!E4cIjbb}R9Lv#j^d@PR>mRq)ZJmC9v?3L z8_KmuSFE#MjPYClkS_{u3bamj_$)HJ>?Aqc?pf(*icgJfBQo4Cu$~AiItb2HF4(`I z1YS0Oibo`fWUjxdJqG@nvs+}C*F1dqg}vSDO$rPq{)!IQxe+84P)K!INyPqtWZe*f z``gtK-Eut>89i&WxemLiWSegJC)NMV*CC;d>x?mO=t<`N{1imfnRK16kw{K`?U!qCBl$W6#*CgI&WU4)paxNvO}*Wx0iN@7`dAK+%&#yD z^}T1{Tg(iJ*o1$>Nwt3nJm~=vv?V{v<3Ic=eYgYDawMpU7TxFG@+at>P;2bGcH_?lQpx6Q9 zE!Lq(UUJV|i;|o6B~^^^gz8IcFFt{3lqwnF>LkVr2_>q(-4>3B35rVyyC3@$df7S2 z2b1pkAi0lG3XJ8zMOF@tK8$r=cAL@qKrxZb?+f_6snEE3;T|!mG&}C4n)x}d{1xifim`UJ;WeDv-e!je zrtuKCPWBs8(TK)~*RuQupWjqFxn_E^sCvsZIqRdtKQ&d{p6>5wNj@n4CF^uf&dp#< z##!&5G+2rnIixZaxErmU=crd7?TtVPR3&jMz5E5asb#q~*d}F{YIT(QC=~720{iR=8lkl@{;#o(v;n9=0bYTK*SQw7uRAOjDZd*5csRWp7%2`r$B>g%_rFzx$Y!Lihhm> z3a^EiSk-dMu57DAmXfgTVeFUsoZTobZIB}LCwJR&$bz1rx1sowX0uVy2_9!nS6=!RqyzBecIz;GkO~AET-iL6&anA@V880#NHoM=CSuy%Mu=Q-USAtQtMNneK^kl;GlxJwCyL*x9Ec5(t z$!erM>QW-&JF#tfh;f(;Opw``lS3=qs&`AY|6H~eERNqKs^~U7r%|rM&^s*%6hWG% zM2oZTzWA!5qqVVU1+?DOm*wmtS(SpnU#tJMJ#3w`R*8Bsv#mNeUytn3V+UD9niX5! zVfZNr0Nrj{fNjS)Q&~M=?74LL)={ZGA7UP%QQiI+nVpMfo*a?;R&S^dL*ULz97c$M zeVaQnH0FP%bgIH{Lsx+5Q;#V-qR$;c*hql}^+%85cfB=l2TQg-uD zH@@W*On7*1*mXG^)&JU)&xFogMZ*8%SW3BLB;#k}=xpeNnIUeBQO7gd?u0+%1sp~X zIM12Kcvsc8N!P@;g^-z$F<0nf95<$yObO;gtO}H z56kK)3mrxl!>D#3{yt(&5H!&F@C2(N=VVAS)RaqMLBX_j4nGR4Pdsal{E?Q)FJQB* zuhz}8eRal8CVpNUgI$F#g<*2Od37Co0MH{S*x;ftotBDjj>k73AsrK^BO^l~J9nt; zMF7ty_oXx+{*F?S759C8pE!nrojc?|@3X2cclT?~6xq8he!Xv)y;#H`1pTV^BNZ7v z-d`DR^ga|P4YXH}^2VvXbnStA=nWO!=;a%=;GMl<@5@S^FWI4Eq;>N{&D5Tf?RCh2 zkDTN_c$w16WIL;Re+PC9p-HUAv5TN~VTo5ko04YP?noNPupT@gy!KY^{=@5ew|E*j zYRf{sBBosK%@_<;tbI~rbv}7X-am6rgnDC7zJQuuW@I+5-t=zDDa!*0;MW$Nqj)u` zT(2X0jSk)Syv><}>ZM%^KQ0$BvH^PsH3bbA0&G(e|G__Vf?l#!@ULFvqhVWd_HB*8 zh=@?6B+1|DVCNG@dli?o@v36i>D7)XJ)HEsd580qhNwD?MO%~Bk6tzu6ScoOzF?Ftd$Ya`q{3*aVp9gv?z?0 z+-={*EH(FZ3ua%lr+s#gvrl>dmjzgW_9^S_r==Q;OX|@2Esc5k*~q@ToLGmlRsjSEAxEwns-dV4 z+Sb*`L0N{2p;&iui7n^{FOR8vbMnaDipxi|QRx0; z%LGSfLhYIzz+-Q{K(piA;c-TKxEjn>y{DugJ*F>Q=iO{2wj=Z3AP!wj0vEOoP3)Ak z=#1`g6$6Sa8uKO0DI;)r&$d|Jj9^feva$H;?6UFRhzNTIkxg7X7Ek`vr){v6v>{29 zL{nf%F0e}bt$=)POM}i+*^EMjaXq|SsBaG4LK@V0)oX!R+F8P z4e~8ptJ?dvp-k}PO5WkVP4x@`$bsi)DK zu|hBX&1X7_q(#%hV z>)$oB_%~O!R;8D!6S>v1AcC~G*`{S_=Dh^+D!)l*`r(S4FOe5sq#ZKt3M^a+_}ew| zdJ|xjz2(DGhecb+x)m@rAT1ZWhm$@OE4I+th@KiV>6{EsTwnIi(*(x`*0rkMv#;fb z<_(QEnRor(US;*J5!m%KjH=Z;D7)dp@4c)&CuBYqoT?`PmCm5(IRa zLL~%$3hHcK;9F1pEG zq$TGAxGkDh97MmJ)&k1XSc^U8P_Mx6wCL6U!ECV5CtYM$0WSjg`IwwM%_|Vj7QhM!oIe6oiye63ZR@)xU(u4enVGO3xwah%=trAi+GTxl$gHM+j6i znz<{pbM6I)6otCZg;&@<>aoe|{_kn;*1ZJ*+) zcLN9PBy{)1AUTBWJ)J^}W zA|Toy(m(yr&MpAKyBRz)rZ5&@Ted^~MB9A*pE(~>8Q0B#?mMX#hcs#n|LA(7v)SB>^|E}JV0|mOg(e+aaUylqkRD$0& zG}CqKgoMfimsL~q^#?kgcSYsBGB6BF4xnvt5_NZvjO9Pg^E{@o6KevHiD^vLe#Yps5D^we!i zYyM}>hs>`a2peuMvNPBWi7-M0Xuk|OnJx_@&3l5}=lg>>QUKC2!j}zmWClCMCHvT8 zUnqSO$?vQ4sE}9`CbN3_bKIKI9n#bydVoL9Wm?aKi3U*sM>fS9-t>S7=ZTdrsaA*x z11JBJRd7Dk&b5azKw3~R5%YajmOY(;Q_n+swFz^hk*C3ozrX5tdL3>1JTcO<>t}dQlvoU7W@U z;MCYKwtN08lVy@Z|Gs+kyie`|^WVRV&aysUW4ScDpF(an0|$U{7a$x+6GRG_zTL2d z^Z#-6reRH;-`B9#R$8YxAu3ARI#5N7RuILIQ?*JJkyd3EQl-cwrV28XV?{+p&CdZ) z5Ta5*#)u4&F_kGQLVy4PB1Axj0D%NT$awnh0QUb}@3&s|kmOAFz4qE`uf>xdoc=$a zzh0P5NVT4VM_2-jglz4MZt~o**8`;xL7jrq%caR!*^Fg*WRG6y9 z4R(6mZ^O88pBnkEi7f)yg0smzbCY295Z_*QfY}bs0kOI)nj87gX1kE!u)HyKvFC;L z1LqRUTo4e?WEfDyY-_q}lSmD#7QCh)S!4Vvxkj5CfAKnrb$uPgW)6^wjWr$lLzKJn zegp2O5CY)<3_4uM!TLVl3DxQo2U40+doULk2W~)s?gYEr;csb!NSTDxB~k#39o$?s z*JxkmHpNRmef;L|7HM0i2J{Isb;f(YW+6{G5F$U6&N!p;R0Xf6IKIEIYZqKxYNA>N zL07N-*Tavrz^ozMUV<b!T(&xb--(p3(^QgLj9w%r0PeWYk~mPun#J@`SOpdE& zpLPtJHVXHj&THQggoZ+>Fm~d2O7z|PPc|5 zochfrQ((y7;IDsqYEC>h&rzbPItGRR-tIkgQ50D|F2P+#y4}D^TI=V8eWUrCWCDGu zrD~)nh=}%3Rs>a8m8jek1boxmR%%+Ru(4##eZ9kp-&K{b$mPcDWDzG|(4;AeK z)}hb_yQC|btR%E2bs3hstge7%`p z=q4*ce$TIFhq|FE|ETi7p1rOeJEaPG>Ys^g`-1HpH)YghCMvy(CyTJi_joajkssw8 zx=QT^KtkR^;(K_(8>>uJsJFW^%-B)*$RgLL>A>|m7l4N8-&y-}^Po0ZpSTeyjVIt$ z{^PYTY>x*t4CShToU#iC2_5S1BmM?(N%f7O9z!+I03)62Y~ffnn1p74 zg%s$Ixh~+T&y(TRQ3l@{EED(ZPN2j4CG&f1s&TXB6|XyWB99e_S+eTb&>qfp>UZEe zp8!|}8Q%TxtbL=J!=1|*#czK&^`~P0`(%?MC)lbnkN(GpE(P7P&v#0lgrV_{6>;pU z;ytR{^YAz6d~^UE{|0LICwhwRBAlMwzJk66#9R@FN)!97<$qmF`ghh-%{`7oQ$8Zv z3+7C$#VEb;)p$tb)96iviyfvlDK|N(6Rq-Kcl&eqZ}QViSsC>Az?@=p)NsqL0u-Oe zsED&;b_-T|)5nBeBD`Z$K4Baf!&}J5Sd>Ykee6K0%^4MpTrXv+x`r3RR-X_|uBnG> zdxctB)lK~IY>KIKo3TFyuJpRsHM=%nl-wSh6miFp?dnrq51@T z>%*2cnBw3jzPTGHS%+;tSDMel$tl>9JIL^#x?`5?QTaxkC{|@Yz%%$>9xu-B$Cjwb za#eGQMKD<5Z+&zMP7)w&)Wq0DwJYQPX~o~AZ%tRk2@S!4-!8KC*N^YF624B$*mOYz z2uDo7EGt^5b0k-Lv!4=I8!%soJEN;PV-@D-{~n3YkM%Xz?aS5hm>^Tha#OIzDj)|9 z9`>UXlhV|0ER8E)r+!0*B6{mkJ}sJIc(rsBBSfYUa8p0nR|>`@juV8y!SgXQ%f`8{ z=o@j0Q5bp-&*kzb{stBZ3jyL0GA(EQHbu$cHd7#}(v~(g(zXT$%*Il*n_={<&y)U^ z_u;tDx^n+DebdF+OlBRFRhBG;`uUhgq#FFzK#>Ild8ad{h?==G=;)e*IhsiC!b04h zj2VnJwNBaUe5Ph}wx8C21LE{4;8lNZ9Pk$j`5&}c>VxDDcfv0nQVQOTnBbRMr_esw z$S9CzleN&QTCbherdEuc14bN@k92FV!#Jf!UH6YQLm;U48QJ1E=xQCQ4FdQ@sNvR)cnk@=f?y}E4TgFl2SX!g#|NUM{yfxL7AmOIE-+#7L36)`*l~}aP06+O?O!$ zXp>*?^few$Wva|5y~^35dfRW@L35@?a834ezlY3ODOAqk2d$23mf0w|y>13;Pvgzr z^r#{89MkJ=?jJ{}NW)OA1@ild(x(l5lq-9)@s^58?^jDRgEO%PN6&B_5#zJ(8vLR~zlGyIe=WnBKYssOZ0$@(rJZs6r=MK)pGS!F5j` z8!8KI|5jKP8bszvjh5Kd^lD&T#(lcDdQmd5o{vEqI#~w~bHAYnY27x%tt^O?v`O`r z1%t)LD|IBXCgF(3Yc%#PYPXGeO7eX$+5ner7hJh_CHC0TJ>XQG#KFk1+=-;=klUlq z_s7EgTZBHk%14ulo6F)cXa9+{tU7Z$R8}}vOg#xS5?A;Zf-uR6tiawJ>G>MB+!Z{z3gM#vf=yuLe{v{A1M{!pfKWI^kIaSV?KW_sJKl0I6f->(3T}@}@E3$p{gjB6O-wqqwC*(>*`b{&)}P znpvlAKC}5Om74~FHRWCmeZr^>intNuT)EqoY*@3(B(dP%S?iU~BNit~YrD6V)omU- zWUM!#2{CX{i^fCSD|0#>bNglE0ea0 zYLbpwH}p;xw1otWjcut_naiv3f~Zi+yH^_WNBS*)f#SUC086nFo~eYeMb_Od#~*j| zT5=X&Q*Lr}IUAjoHj1V4MTk=ee>dKFc(L1VA8m-yo zX-;WlMFI4psW*9!X}vA-JTlcmYf z%(pDg6L-QE`gQHKRG!KMTW`?HMuqa79xxBK>qFh&qWN`io4n%!c!#W_ojpPavKrrO zko8Yqo=x5In|(mb_U4Sqo5e1rdL7V^66XwuJ;f6I{IsckDton6_zR;s`y~0po$Ih> z$^aR2aeb}q}0i{2bqg|cIJnVl&9hJ#Y40Wk0u#$rRAg8hQ z$ZY{m$Q8^0(99XRhw4|f$q#eW7`1mlF4*?=UX=Cofg~?-$L%bN@o(elhx*EDEj#!J zQV$fideL#H`)J#U)onjjU_}V!rm0o!)|$#VW@KKtM`hwhUcQYIW+b)Uux5Ly;5u0+ z?34jLJ7`Z8X=foQEF}kTB((>H{zKgkGTTY@QPzu#9s2t*0RxoSCkD@`Hx8VcC2Cq|hhOO5;RPUy!ET|mUHFAoI7=!|wd1Gge%nJT8 zmN3ep{_iG&pxxlROj;kf0^p~%R!XNgG8hK*@els}TjW62xBm)7^a-zJV7J+XB( zi7Y0TB6BM8NBQF?bDNQFTX+QXykte}*0WOjFe*>eJ{_~cuBqB+7_TGeS0PJ@`kD-h z+3S|hZ^UJ#@T9ykte!+A$Ag&gV_x9ZH5e?-IoUD7miy_lcnMzS4o>m#TwT0nuvFYh2ka7)Xdp(_R;B{w<)OHK^*So(=B`9Y z8MiSKRz-hlBl7qtSnMGG0yoQ(zH}=qAIVYg|419Ai1sY1Q2FtP9L^AzB=!c`hz9JR zE4B!X+9+A^1p=|n!D|;|o2m{+rRpYUN?SU;oR7=dnsme+A4PuIU+Dv4i?DftMPevviaLW58p0Ip9{%HjLhpnql#iVX;~J{s_BHkly>N0~3VDA`v(r;9Y19XO@e)62L zmHKJ*;tKeYis2vP@SX5!LGnobHAf{7RFI@rrL#>7Q|+$IG!FN2 zZlp4j^MUgIj1e+EgH%7Ba7<%kwq%b#>Ib(K0%Ek)|HNqyXFL>IHFVlUl`jwvkGi@y zO~Ax>?8m^>9`ru`YYjk|A^S!H{d!7S4W<7zI1dfA7m0_+bOrvhJn5Fq!R~%;TS!QF z2j|y%f7Ef1QEC95S&$yA{39sM_GMU0(P-~DVW@KxGmbax z3UlC+8z~wQ8z>;ofKhp6?NI6mXfiVHs&-l(t~sG`VjiW(^btFM-VS|gB|xhOf^j~O z8anmi&M*~PixbhtX;^jMmWdGon0YGjx2H`FmJzfjrW>!cR~p!NvBBZvWO!drKT4-G z&WI`M(_~TVBd7?Gp`2bvkZIFvVWc`V0L$lHvtwDKKUIQvB*GY~hYDn2=jkS@E8T{R z(1afjq=zg@C~{MagYYNMyAUyG0?qk!e%B8ts=TxF!x6@-bIH&42C9ZZr<44r`5mCY z6D@6Ean1ry_&eHu)a>3eCubucg)W5Aro{h+BZ4X&{x{om1DEnNR&!uwNK7~r%R1K{ zcGKZuz>dCLIu?W`Hq#%mAf^AmTi+5U7-B6^4<(;Kn^1s^*Z954s42?m|3Zp?=_oTKJyWI8nFVW&LQ^-MUBA7rAud@ z#*GPhCj%xX{I|m+S#ZVIskgHaYZlAhGK2i8T-Pvjnt%U7FpQoqR>6GuIODdz zdra3@_V>v~M}JfiT!_(1!d+R(cah4LO}}}W2C$5FRA4`1oWM@pJ#q{1e%+K2yl>}D!CXHHAEkB&uwZ?c2!C06Y{y6Cwxp4q2gT&RHl!?_awxVDs z2bs}Gb6=a->$U9QhbLjwOe|WHQ*O26oztJ@Wb z#hry+#f){fIV#gvAZFPE^o>1!FN{-=8-v~5optcRW`Bs1gv*Ir%$OEe!&mGySL z%uE6*D~8Ts0P5R-Eef;9#p*OqiO$IYWF%Qo|1JQXHfIIe#0Qx3h*7s=%kc{LlP{>s z{CM6Us*(ExBMF%;11h@5M9FCv-8g4Lg_ZCVbs%iHe6MO5RwscHQCvrVi7$t@d`K39 z3hDY(qc*_3zTLmQA^K2BuuC#41>*lHY4W7VbI}uXlO?MC4c-6Fic0Ijf8$l4q^pD5 z2bpm@C0OOL*Mtjk8j8iDm(GOI5}&1^u6}&hYqaI3@_(w z>XlzFw>EVzjdP823@Z!8uc5Zk#nz0!@OhCDNSB!yxmsc-4(!%s^{dy*;;aRcCX|I} zd2ko1xZ^`^MaOo)o>Zeqg^ljA3)V@;XZeTQcNzXeSP^7Z0U4^zTf)da&uRa)mPzI@6w=yn0pZfF6l-?b2jXH+$0YB`EN{ z#SLQ1*Jg3m@#4t6pyO&O%*M&nuy4`cICJz(4>?7Nh;mfB<>$%;`^89vv_lWfnw*fx zvSsR>kob+{BLna*ec#GpT%hZnPEI`ic%Z`Br{cSP}HjSPY5e*@40noABnI?ZM?_uMm+tH>hvgQ9irrvq|_VCzm)?0n@YkV%Uq@2Xc z6As^OLJa#ckGBGR+0HO$IZZORr=0cmgfrz!HBRmqEEC+q=b_i3V()(Rq8ZNRBSpE= zsFS(?6QFY-WN~-HXvuMjk5gfj30`DH5c#l%ea(hflQlm{3G$3E>n_Npu|fvCM6Q_Q z%C23u4bSft`}~o2t}`BWGT7*@M>d@#JS%e1O-hL&iRJpLUwqK~pUHgs7bZwH}Qt08(D+R*BBT=6e;%o=VKRI}0_*=tY3! z-^=bp{6Gpi{#!RLnd9Np4%WfVg9P1g_b)k%)cNJ|n>DXhj`&o1r z^C!kuLIIIV1A>^4xOjHmp%%c4rz-SFHoo3!e(*EAlZBcCzZo9?J5Q&YZ4V z9~DyGj;Mcw>NU&|P8Z*vQpk1UnbtKc6Rj)%td)nFz5BZ<{j_dcRq{o52u)&T;PF+f zPQ1)?syaAmR@wuqW@Wvi2b&kF`T^tP@g)b&OUl_FN20n2kx+bLRoGjxEGH@GJ{#W_ zK9LnjJGYzB7kaE=G%r-+W>t2%uRYwzp?M3#c&o~r9v!X|vHR~ogGvP7Rl4FGSnJO; z-!H~i2fTdg6xgC{3nu!;LgV{m&*S661+G@iiBtj25j!$%Ukcqns?X$z`xdie0cwq7 zhsUShJTyQ*8t?mi6c&%|(uAOiWzDV>RRDyxvl@$TzngG-JN`ESfujILVwAM4!W*04NJ^bk{U0ezp?uFQB)pvpi@1A>wf>7anY-TzhC= z_|R!jI6{N0bDA0^{bpJf|7e^#BuG_=G!VtS!>D0zIls7hWQKM656Jp2_54p!I}Ku` zYfj!#d7wq6~X;x z&59alFfnSv(4de>y+z!Z8~GNRX=3 zNo9@+Z}#>TC$2(;k$BKIO0&(x7gL#_a#J$72g&Lpc-^ER12mbQC&$jprdnlXLv+{{N^IhsoUlHr>g}OQ=NWD-t z3#eUG**+fRwD4#2`I#gH*bUPqrER4O$98W23*Ctihv8S{~8dD>JDI4NhJy;DFtSmo~>P)*H7 z|No`qNeit-L#a~CedfkAXipOd#($Pm(;(3^DmNHZzN0VI?4Km}`z{MmBUxa9qv>~? zisNklPpkIJz-K%SEGg^%?QA$?U;00h8#vm|<7E3y`RZ?hwr>4repIUudwRex(vbeH z3hh7>@_xPe|Mh zraWzht&q6kJdgyWm@v>MGkS-a6_kng?Ww`$=W4l=g@{;x48xyS85l)9?@H+IPGH)x zCfsV(WYzE1-M?fTssfpEbgSSURE%ZDHfb)wz%3Pmo|xl}C3R6}|06b-XCgi^P&iQ9 ze%{7bjy_V5@2B_=hRT=OwOOB{b znvuReA6b7X4YkbPzq5(}AFoS6x}fWHWj?mJeQcD5XMT{O?J|;XS%JZ~`MD~JjCmcB zo;4BBxy|z93)@+%Y?B{i{hoqnzFT8HXud1oMYQe6Oe$)ZjpitFGSS!jNSl51$fw{V zY0gdgw1V&`Y-y>}IJVKwK)C{T!0*|SLj&|{NWqzmF7o^jK5=V+?j+8Ze9=BWOcuW= z+22#^(T!B8MwPccL-6W#sKf@=iMzSj;!&9Oc7J5Lg&CBE;)ax#wSBgOr?>k=7n9L- zZ9qaI?fsQhq9%!im)loT5y{5nE=^!1-B*HM58K~ut*=3>uqxnA`?0T;_Cg5BJC}7# zKZAxx-JQ|$SaDg7CMu~zo?A&5Uy=J%CH~N(ntuTLx^(g=c@zE52_johkelR=MDL&OK7zyO&~r_z{^vxZgwRq&NkL2_bnxu_*%h|MrxkW8JH7sDlQp-) z3}xr$5o7F2vzYPf*iV+Sq)K7vdS2`VUxEOX@g?w2t9sbC^1lvS`&1JrI8o9BBYYEQ zgMg*|ch)3eaSNj!bT-DNFQe+$WCqz$mJHf~2|pMR9fANbHi!usFS|aJtt76EJJjA8 zijpt1j_1ZD3Swh4JFybl(`>|(bDLEVc$>5st08|){H~j&>-~HYO+3_Pe>LlitdVC) z=CZ=<`y`2%i^-Si2 zQ0Gx8S-eWduB7QZ$SDV3T~;nU$hUzlVV_uVnYhFwDTq>m$iAvqVfd?5EA@7Hn#9LJ z^OP~cxka8=g8H8K^GJ6L!u?V#F^oXF9OYJkET8Z+mQdo+gybl3pPuet)g^)-pQro$ z{l~;;zAV3p`n77fx?tI=E5FqURc6d$eX`i{JIN$A??w+zM}U^^?&9DFUG=$AeNGNq zWeD(CTTnE4sKA2Ia_EWX7Pgz(PR5tOcPtNVE7>6KY#hgoa8dZh45Zs#fxh7|Tc$DKw*wrgmhXGYhznOipm=*<{09Ty&Kr&qx+iGg<$4JF=O(l^cSR23emDu)1 z-a@512iCY;OMAU$@BO}qo>uo)G^Tg0 z+Li6)^}gqCsz;T3YkH7+%}eilw4#J;-bBL=#hsGKx3O>FwGwnW0J7thEQ3I_M3^b* ztx(!a4>~&YKc3n%(6fr1lCHvZKCQ~%V7aQs$D}lxqbB+?r@VhDUeSyQkDoP`#p zOpL6#dfmTM0Mf-GkmJ-}t0sSp7cBe$2uy@{sFn6K z&EKOw#-bdpChSq~_*i|3oUf;corj*-0Xg;= z2tyBiO6a9G3}*n&6E~61?O~nx>rNt`x@Jlh8vRrUB8g7+Kf3M&(K=HKhpZ@)K)iW? zc*F)4tb2($;>pSOYPya>vzyU28X>^33W&SEm5QR)>KmP(bB?_Df#@b*$A;an{&*sfoaLO1k z>piL75WQpsr)0R+KH_mDUvq=>fcC|GJP z6Q(^h{+=5)1D2bwL&<>jE2!UJ7Qf>O83R*LZ2B72U+A8Kz9Q_)lFM~A6L>5;ffT8h zkbipJoqnJzUQwyi5)lo_B*}p8|j1!l6t{+ ze>u0NzXJYxQ7e=(k|CF)mk1X_ZE|b*(4+&ohQEt{PLCCCI5CQ=WZkU{8tuAWB7lw} zlMZDZ^#ht{1SFKMSFV>H2_OfS9dUD{!WxE3#DaB0KOQb?2;4O#G0geTh_BSto&)vM zrEu9Q7cahcxUYW-_DXT?$C^V+?aA^DfP971$QOX-EmIObvZHT$$l?rMF$q5_+JEgy zAaSpH)UR|0*yb|*NKVYi2kS2CPJQCEEs!}h@GngxPygBUnel!2$NoLr-gOu=24?0X z0(7Qt+%&A6`^w!1VC}S&q2I#b7n!;;QWMyXCT!7i!+UQ`wSoQ{pbS|#<#_bpgvEc8 z--YaAZXx}E4mvYO4K?lV5Auzp`y*YseUjyZGh9&+oR5ZbSVecxq!V!|!T=d*O-^Cq ztGrx5Z_tcH>F-!`BzyikALbtC{xIN^Ff}{QQtIp=s^BH!CjZB+4H*))DiAguA9koa zjYyaJn%Ss4)FDpLsQ_!!p80D-HFGD2D}Gg-*@E%3$!rprlHIR=?AkSW&zWpxY|K&^ zRIj+0i@Rx4#w0^Kjp;zv;QS&k$;o^F`99_Q^#4+ zC-h&g+DwzoI%lH`d5#Cz*N4wE<&pDIZ&%HIEjf4Y(Z92n2^5QZuxmKVVL9Ga(wC8q zb)$L0qVTMK{*)UqL7A$V%c>(qD-Irt4DpitvtR}LQlqmi>FkD)(QGDfLbC{bfGKx0NA0m6b)r<~}5)<9k88Dc2By6_)YH|>z>V*&VKSM!_K=&@n z#Tr#}V_}SnBfr~|9AYU-R$cIB50-Dr_1j_{fEBE>4Ga&pVftwvzFGqBpx_ozvV<@-{E({ zaDD*Om)7*-$?98rr8NvILX8_ujDJmU3;l@QPJ(EL_+-x*!D6C&a5`FL-(ABI5#> zmBo#!f06K2e(h!Un!k~AjLU*~(o*CfeD2o5w3EU~hnHAOu~;CBRa-+5mI+smg@uE; zp~n@Y<5fBdBtiIjsn?oHv2KWm1HTLCj$8xV)*V+HpZe|}!X;cnpwv(K% zZYMjGd#toH46_k;^Fsq%jK^Lg-<9B1KdY_9A7Un3%(IhIgff;y5lF7L8Qz~37N|)l z%~3l@>@vD7tSYE_MdHrXk1QQ|WF>xVi(rthdO^KhCb+r7d}ZhI_wFnA{Vil&IJ*78 zQ%vtn58uk0GdI!xSPVs^|KKz|;2w;ezK5O}KJtZngZy?kZkA2Ag;`Y*NOjZ0bL|?p z)Bzd2Sv;sroCn37=bt0CzU0ltqkk}^<0@4cLpM82mEat0w8vM2K03XmPkM<4Yg@GE z{sq%EOg2IqgX9Z2)c?V_8&9W{_JBq9zPJBB`Dyg&OIxNi^V2lQton?_6`(VP_M-31 zB;~g|zsntG^l0cCbGjcm*+T$LK*Alg zGH&wvQF}T8~>Yj(6cG&0b zY_CyYp;e)OT>t$dn__d;UwAKQPG<>p8kY_32<}7;m2L_+t#>(ge52_W>}!;I8Y|3Y5?*`X}q5dweZ8PQkT364D?F(_(g=*Qfi^g)D_}6!$%V zQ+hO;OaI58&1`wTN@BJ}{x=&I=6f*ONXZ!!1^%jBO zrm=1Eb~=l9AAfS0&t)*5EA6)lMt8axPa=0YUa|KFkA+9R1#X!@fdmyg!%2x)inG)H zc8Y>L68!0vSQxr5oD}U4WTw0b3p$vMeF`hMXcp1fVIoYS2fle*B3ZMH+8qq6IZr(f z@wVfvYTUfmPA7in>wjla=>1{_WuXpVDUy@LCS8R`<4;ZsIAu{v7aA@5=||it^)*CU zfp00SN!+-yhd$)`NVU;X#>)PbIBgm+J*iWKqiEgacg*-ePSgiPPGP_yWKvuMw-53= zHDNGq9>LVgJ|RO?#Q2m!pnSy$EI$(4rAztT~8J_k!+RGX(g(;>1^Dk&q z^yX~W>1?lAi~~~~?30-;MAEcsrf|TCzhX=8W&Moc{>y!kTSGQBCLp=f{+>^?1Z#3g z&dGk4tQ>NkrvE;zpQ}E9K<6jBtB<$2x;VcM4flm@j?rp zGsrK?nRfe?(jqW@abFH&FDWD3*B7qOR@8e!h=tg{Js<9M?4A490(x}5uM})}^NS2x zoPRqO&RV3oD?6AY&PHDZ%XWmPXd574;QV^RSm*8iH)BR)i{`)2UDJIm_?E{aI}s=R z{s{Y#+XGo=jjt4oE{>P2mV(yxNE!$H5*#JL4aAmyrDI^ck6zbqqalqSr0e z_`NP7M^dVZEq?IxapUcoLAYvzU2?bXJ-7YJnSwT!cdlwhju+T-*Jub>5*^F5=bYJf z$l8c%K<2$sLJIQL4ZcX$!l+uS{l%}nZtW8s0_2Qz{Rcl*Jz z7anRbh2f`qD@XLp>mG*>sa2?}$L&Wa?jU-ejz(sGWHIkC0d+QE7bnoQ>Mi2V+cF43 z|MSD3o*m4m8b$%c{$!|0`*+r_NMo5E@8XRV)+;&($U4Rzn_lv=d%xQ8#bX$-1b1aC z)#2hh9m)VK`@*=-G6k!Kua>3*k93bZt_f6X*9cwdVd*rr)+3Hn2xcO@97V5^Zpj-e~h83hO;*5=8zC7R{i(GCxxH%HG{p&(jYsG|pzE)LfiYcxBz~xKfk&dr$I6qZEsl zX%=OEAMUU)&kHdxnG_7;X~IE%f^t8=?bcc_l<1LAF^mi+e$P~MHm1l@0lQ6Yl*v-H^`5=#FY%0wASm_`v68tq`g~-!PDLF8m??sn=B__cxpGL+u znTucXA|;Mxw)Wd-J7VUkvyAf3u{T=Zz&M<-TV_AXf@%%ZdtDjs+&&a~luJpow3V4h z74AA$=wx`AiXR}YYu-{~_Ax34v;q!Z==f|Y=D?_@wNhlC>ZPH$`SLl{=6W}XmSR@9 z3>DvU^fEBWZOH5Ru|gH7LMM9G8uh-~yo~e8YNsvZ;>A%T-&FF^NS-89*t^g+rH4VN zST70VT2x)^8&pn?EE$EC-p71UqFjK6nWJNld zMs}(5Ex7ln&WxCL{DXd@_S2=gW6es!5crOZvYBV1mh83p~5por%O|R?Rjm4!U5I|s4A;A%H<10f- zz^cFO6|iC=KGUYL^z_=D)oXR$B&tNnC-kvh`kjo9K~~m3fFdg67OXgblDrqecVQ^v zJwEMm5x!B+L#RwV^Id=oOV(KRe+VZt*I=eK?;xG+rZtS127hyJA%o}~b$ZAEvE(rS zDC_AIUFbADP;PK$10@yZVY72Q7J#Y;zLiKdZE^(I_m}55KP)4~)I*Uja04rMvc|jhLqnvsNC~@Z4MUoNZxcwh1kBK{e-`G4 z`zzLxXk9f!g<;o?mEp*di^}udmeT!}VuP$NW5#X%&>>P?_Mj(c^Gg3L+%o|Ex}F_d zIiwW}yQ7T?-y1y)9TlOZQ#4sT{Qz_v;8s_WKrX=zxw}6xk(py+AIKMqm@)QzRKv$ zdC$v@JDK6HT9?LYbE;_+#B^Go@{sJQUU5Dc&yS1ciQMu%YI-P3@N!A>IbYUM3yJHV zH!qNJLP{Dx)X0|smQH)u?8#u&0X9SS@8fg_WMI!!!#t#4oQ0EHXHpOzI4g(xYc`)> zb-I90*h&|_5h_`^FP(fRHN7|TuM=VL#`l&C%dZ)9-5mLD>|3pr=M;v{8(%R$p{-hz zM~#EwW2e64KUj-+uuaJiA$hRETWhC~Vw=uZ0ZLFJO6yV{i0a>!!okqB#$4w9 zJt}kMIOZ$?M=TKOj}Q8X&z--AK~kdf329Zx8m&lKMe+YqDa}JXs&{HB9yL z`La3k+v1~xf|K-aLL?FG3e7-78GCFBJlkXMY|%l+^a1&x*ruE6ZCt9McbWc+CZZ7p z#mJP&{Yv+6_Lahn>Mq?yuJ-tV7ExJ@i+$|JKhH~-|C#f^MaFyk0O?u}_~jQmr%+|7 z&{z&R2*h_=+p5lgvi2pvqQ|Z&6;v7tS?80T;TEBsjtiiLFy+CMb2PELA1nKJRvkjO zIlQHKw$$=cbw*MQU7W|=g|S`OhgPHIEw-lwB(@=y_QqAZY8 ze5HwmJ%Rp?nMhefSahbz$+~Uieup#TQ;@^({QfKVy;2G{gqo(eW!b-xBiOvJ;DtgK zQ-3@eq^fiR^E_ju+q5q=D+sA*&u;~+hF*p0W6o2wl7_j1*NQ8uGLvW6a>Lxaf=n`o z!gqELL70cAbE=fAMO5>hz(1$IOBqI(_x4!VU|x@|{Z>ISI zkw$Q|1|64XEEY0(z|s=Nxil=_1A@bj7qllm8Qh{aLSPqF1VS8wV#tMk)xRyJXv~m~ zZ2$~L>s8p_2<-T4XmxaAv<&wF_Nq%T!yS5twoq;tZZ!-k_A(zTn7^qru2$QJP=5+$ zRT}S9l{3wj?UltR5M9wD_E3P7pUOtrx5l+C+PkCVy}*6=Q?kIGq4s59Ofu}qs^{$n6#rqQp?5OEHPiL7TPM$_`eh}uf6s&NN(Q!3O2 zCjZVF=m9K&q>c@T;elp2>$9Q-85`BY3#x5>n4RAO~2-yYIKPlp}$@J2BSBwbcn?wrz3j@Xpn?wE#$<3a<)SL4P@VdxD-@ zE5i>Dp~_H$=>8I`V*3ErM7=sycUPuuVV;I$dCF`!mbg6_e@>$b!b(+J2MaDxdqe=) zCHtyOo8c<0Ype0wd;+E!Rj3OxhYk7xohqjVELKh-AmUN3TH=aN3pxj?!|#K(8ke$g zV@B;2Zdjr3(p;e?7;hO_o7R*c*U}N|N5|ez!o^B($jt?g7u?RVY7s%>O6KH?DM?|?TsDLQYR{1Ty@*hlCB8={ijfxZ9axu|2UAn z*4$sSODJ!c;nTmr&@M^okiz!+7ya(z!1QInifK~_U3P&cQ+-1khBdw;IeRI;y+$z< ztwC$(3hW|%CJ-q5ex8xR6q`0M#oF72O;K&S11U_`=h6kJKtcCVLpaOXkWgFN#vl@> zA0}MTaIGP{^NZ|T2jM9hH=j4*TGI1m$|>k`FPZn#3dpxh`06Hm%I9{^Hn49f1YijA zW}WWOenWfaO(`h>*5wz>n`v`Tn{h>-F08xEQs=|RcV?}%vTQct3=8wEjIE9`oBdHy zmchs<(iYoJ-mAIF#}0m%C3lm^vLn9YrNnuaW!M;-DjfQsPy30T?CTAg@i5XI$o6|O z^)=pL=6m}8l63wv$xrgV{;Am~yy&Z`L3uAn<7nEE8Fb2USUX!Q1+i2z#(9d6Q2%82 zw(vKQ%%{zl{%$5Jk$yPztj@nJ*rqNnkq(PM2-TqZsYlWWSJ)DOFY9M2l=ksAst?A9 z2+koFn`951z1Azwvi6=;E6|>5Ew5-Z-p(8~>A0Xz2Cr+*623D4?v74crjT`btR--7 z?Owa5wV@X_|E~{pIl&D1T0xubF;;HcIrU3suN!SFtSt;mAHb>*ta9qf0K#X=l)tKW z#(&k^-^$9*20Y@N0t{k5Q+K4p`Cww4GdBB^sRu=_Gk7pw!iM)+tq;rXtRVAntDhUB z`iNYW2fM8&jLfzak{fh}jl@TBZM2UFY`>nIm4ii;E2tN(eMbe#chzqWbA-*HlJk-I zx-<|oY+n)ebRY~Z-z!H``Ie#I!=DiN_q#ad zE;_$9W}Xc@1iQ$aV`F0@V;pQVWrc)`83M0HkOyKZbal>97fm01?Od`uILTQPdAOj# z6dZyH!|{7+Ju2y~=JuMO0Em7~yGBHAq4o5}80T`6p}pyM1=jY_MUT37q`hn5+A5MY zA$UN+c=TfuvV^)m2v;dHPrefm+X4lB>e4aDL))DauEo-n3*o2aU0) zWsd-EG#0-yyOLBPPdk3gSD#rB&l1Y^jNhv5d-(I;*z%atd(u(L&{>*De~6O`O97I# zey3k2wHt>qd$IH6C#D|x`StSg=M7SUlp%PHp)p3STcrGzC2%8F8fDw-5YP=*B+aUM`TiP~AUmSKwR?Jac^CXXb(GUbJ z&)iD1HOvlatI@ks`vcS6mnh4Rp{M00Y#C0!1yL|`7lbp~ansmQe zm{S$uUJu%QKGjru2xtL)9cUR*bnIC8U!YeEGKbzgV*>IJ!q$I>e*LBEm!Ko`-+X)Q zC(3$BPgUU=Peq8oK++NEo9RkwLt*||w(*_fzBf6sUQ!RM z++H2K&39Fy>W?2{(I-j-X?Urr3D!)EoSYimec!+6-0_|e^C51WSoa6$et#JI_5HMd zs%k6H|GWT*Ev*S2iO?BD_2R^A+McWTP0UFF>>gN>tRcwxIChKosjwP~Exl>ok-$O! z-57VQ2XT|ANBLvu6zynY$9ixYUog-&))rE>_~JNsKxlH%18?Q=KQmUw)ERcAj#+t5 z8fox>^~>+Rf{K#xZyfO@<1=xH6&S}&>OFWmV@PWReOp|m0Yxx$Gv;ch&#(WcY(D#b z<;f|7!I%2^S&7?HssAP@6j+!J{IRZSMY{~va2kMiv-go)9}240$9nhEcH&XZ&T$7g zht2)5tKWB`6%Ft+L4ZgEuRY`3&E%tFW3tNfrvqxc*XFwvD6ka1PCQW$03R{x=?~E3 zD66Sr{``;8@MS23BYJM*4DTrm1oQmI6qzgGd4o!AS~%^-FaBMQzN014hkvDGoiapi#kIj&KXVQgKk(F9!RH3HrpOL6bZpe@%njEZ8b5<=)}Y+$}f!P^Z&5uD!vRiplmO~@FC@p zl}I&2RPWsK#!~vrrEXhfZx%~<$7xk-lKwL5L^C^;FCRt$w*Fg%E;tku?y9_1-q?{v z6k>4ZQoi>;vGjlse-KP;ETB?hlbo4bbBlWexMs$ims$hWo!9fPg*3Z@SJ$5&eJAYf zfM9?@0?kYAp!myQTb<(fw zg18J)IHE!34=giDL6jBp8`>WFCJP5)mQ`1-Z^o#NgzUIO8dUhyU{mXODkYFD66u`K z#FLk4;rfm%dzKT$gRulsJ)pTHrKy;D-y~FPSvxM?9~Zg~mt(2y9PzMYRElQbj^9MG z<@i7O z*F~tRRq>=g#GTqW(3boNT_Z|HNX^pT3a`qL;rPstcLY$j-59?k5W2j`IrYmjG!mmNp|vlyB6`>uS?38gzP8#39nu2sIh zF#vTCbpZ6%_9^p+01ktk795hh^bEgUC?y)b+T80;!s#%7HT(cdH|Ck8?VRxW;Bgfo z-Sj9D86A2}6cLFppyQ(77q}H;FxsSojn4#vg;YLWYg5y)A0C zdG6&s41&Mm#sNaGjzirfj`Q%*??7gW?z|KA`CxTu-*yDV5gVr%k4X|ob6EVs40GOh zh(GFkPbhSRvi1jyrUmIhAN}ATGud<1sJK}ACw!Z$ZjCU1pQTha zat8DCSpYks{cM*84f{uETyDCCykUyVC^FI#{8y}6g8UW(*A2%HwiL&^w=Z9T{e}c`wKnXrMESP5UUZ4^o5!ZVs?ZP&j_f&sO}2l> zSk+@spjuBv`j$h+Qrv{!%xQd-dLd;`hgI){SNQK~Kd{#t99JU1uLiX+&b;eQx-F&ri#TJLL0F1PZ?NQSAT0ifH3L$zX2f9~ z1o;o+30ypJRZJGHi_({s{)WYzMQ&H8SLRWc6STV$0YSapX)1@WW?{Spi}m7a9vl|j zWtW>ax{{3M*=bP);m-p2@+wWh4F;gG7-3?5hV!OkE~T-gCX^aFJGm~a*ag_lc(QNW=xqY?BIr-gl} zo_UnJ7db%^F&k*fIe*3P5juH>hbvqTU@xtR*X{uhZ2nP>zk!TE++M>27&<4>_=K2} z^2x!?j~e$AaK538V-sbWsnjj4e{cFDJG5N@^U6lgehwl2z$J=sU8kD6REySf6^;se zPtV&T)6JieGVX(j)i6!E?2?mfA$l&>xwo3Sq=r#$-%@rp1P~zWmuXo?bJy2OKXkvF zcH%U--5v4{ulVu)f+$0iaB^|2h2ELHG>RoLGM`sr%DqquyCFyKYRm^rp6ajU)d!+! zsNYxwg|)l1th&r_={+mj$HO88!V(AzsB|43Wo5nGXydF_+D_~9>9GO0E;g%NRai6c z=~^ikW6aA~km!-7Cn%XpEG&(uATaHpl~gh2WM_(UnhIR`dcEUf4XavOt{#qe zOJIlS(0Z#SA81jQuZTZH855?NAMZs|S6WTq`KuF-C(UM?90Ra&As0ro&bHEi-{LJT zid}AkWmg)ozp6S-$WOqWG4As!9*NBH=k2KPjY-zG?z4aZtp_}l;e}|f>l+9QG`J#RsiK8k)iI>^~Y7B{;@lL6KCio5|ObpU@=d$KTm;xC3G=9`+*e#iIya zWsWJrOy}zIZi^w4ZR!Fw+{x9}fDz)H1k)Ng5_Jqpc0A}3LB3PualC{5vOTNTR0^PRD#bfsR$8Ht@TN ztH>|$vlRJ&#SHVuu-B-*@@HX-DRx&$`$%T{sObd5CF{sqFAZ5-j=CUC_&Evw2G^@l z_|h^`2KCg0@pxNKkP_~DLI+PJ1Idt^ZIQ~%l9TGVnE2qX?Cj3oymHl5 zBYJaa5qn3aeUMvsY~^xnmN4EmKSbOe{UBiPV?Yo@opC!MwY%_P&N|%8euEsBpe}HY}z!oAk@uo2i3mdiOPRE~UUMke{Z)L^>}&>!f&;rGQFoJX@ZbuvWRL6qa= z1ceSm*+YCwsTFRFmpeS^r4-ZC-iq}oX~OnxuT{1=OFfGy5x3hJb$=8Obrn=`-SHf| zG4-ZWQ-fF{6@Xax8p^rTXpnB10KM8?QvV`lXRj#&<382tSNzfB5NaPXRIV;+J7io@ zlQ;%rzXp#M^X?(0j4Wl%L5m|QFG>=y!>XE0E`Lefo)RHxOY`5-uOAgG5(>()5^;g| zpx&Mg(%c0Y4+~?0cv5A^r(*vTi(h%GDpUi6zT_C|b8#z9Z9d>Z%mgk@HSW*9Pp|46 z5*QtQC6lZS!XhWzfeVrDOk%I3TnfwEI&X-1!GC2<)dQ2!VVn4vS>{zLHU5RY>J*Mj zfpJD;+T4@+`bG-M`*f_GJ;TcSwNF@6%bfos+3v5#w~Z#mJd&ZV&bN;=#T%=x>M>ux zL#RKU>G1OQ^l!qr_SDX3V|-CvwY9Sc;n;!7x$qeG7mlyhNwE|Gh9uOYrTZrx!@zN{ z1OFn>;T<=;#D;$y6K{>1d%-@V_?9K{GMfRopQ zX(wN}Gi{9e2)K*Jh$Fc=E>ABFh=0f_E)|gl;h8u7wNs(u4N0}QfP?W<(o?19^SLCx z2eY{T*J?YIJILiIaYl>$X-M^gkC(jnS^j*Jm+y}8bN&o zpUu0_?&g;-7-KZ1_8(FE$2I9fXBWo^R%d`)W)34ZcVby>LVc z9$Dw3ths}5Pc}#c&h(w0EF#QcAi>;E`bH8Y3{?DUTuHbSaYJr|4Lr028Qnk2&MOnsrev;NqRrwF3X zUw@^Nw~ezoA#1hxz=eHBJbuj!l8%}}bDjkIs5DW_Fh-sx1l5|VV6q3W>oF|nHfkLA z9hDu{q~9IQax#;-A*BsOn={$4Rhbh=Hlq@93f`0HNimHpJPWY=OrONtung`$L74yU zMb)~XsI5H?h6C8$GzqjtPR9So^yi!N!k^#{yynqE3>*2fv_^3vwEhmYg-DchudyGd zJ|nW^h)w+CrXSd$PVLYOdt?-Lu5ZlB3A2EctX`l3*6r=Uk0&aDbg3n`4HZXjh@=89 zAa`Ore!L@dHtQWal&IrPm}Z|qsk;Kf{%@rUJKQ-QJz|4b`YZ57;B9XpvP4E$A5L?N zpt4bK*`Vx)$ZweY-A`mMKPE+v|IL0SOp_R}U79MC#U>1rM?G2f=v;06n`Bmh&KTMs z2Dn*WQVdT54`+ctBYIz-oYC#v$z@YVpX!Vwc`=XLYf^nse}6B;u8Pvf9i#7$aXPC3 zI(%5;X{E~$e0#o|{>LPC+4JhV(e(A5KpK6e++r1d$^`dEb6z;`wmQMYK%mX& zIi|Mz90P4Kk@0NO->=)!-o@TA8O0K#!Hbdpi5`&fRSEoYa%SEw39(JSH^Jgu1ee%^ zQ(ek~GIOO2P+xBcoYZwK3Z91j>SsXNu14;&RdM8t7i)WDLbeg1j{fW%uwP+4{%V6{ z0%>wM3h+*a#GMFqY?JK`a}Le%RTzq)$t3dh3n;qM}CuSwW)BSRcIehscRn!>eCLw%{d` zPBog9lIke%@}6ollnZj+bh}pw%ETRIuCMX9 zME6ShTc!lnKY-WN5{I0N7}x%JG!}#@WScdJs{Jks(t&*u@|-Wqz3WwU9%NpgPV0Sg6KP;Wga;`^1-DP8^cb z?aN;?B8E;a#3OCKaLE8%`ds zQuVeb`jZTi2U3ZxY%{E{Se3S$-kX^3qS*O;aZPzq`O?m}qqhe1s{ZN<>nNV#qh15Q zd~#qEi-GJ)ID){7pNc6wUMw03BIU`GhDHCSqkk6L!g|cl^Yiz ztEE1KIq^Rr$?Be+2RmR#>=`#R*_`uM?X^Bono`ykBx*}rBKggKBBbLGZHx~4U{SS& zYk3x`)s!NAfUS6z{2(|>2>F!PL3=`|^^obD)koYl*t&PEHO1niHP>^GatiW0^y{=C z_j!MYbkApMSeXIDp9jEp#6@?sl4@}cB2}}7b(^O}GddU5eNFWM+A%GSeuU&Y>5RA( zDF_KFXE)oht=nEl!Qzl8IO~MmK<gl$vShH$t{c-%WdnLkm6H=46 zR?;6m7H)AZjF=JtX#pc7ey?_2Kj+`gVgJ@3!)6ZRzJDdhYT z?-e+k!6Np$?h3?Fp(XDy5$P!^Y+2N#7af&~zuE z4fOwXAsAYHSn}=rcJ05Rf56Y4n#cwX$GkS$>?u?^U51m)xKm-){w_TIzY3t4NXMBn zLrwVSg^>#y1kbW#PSsNqT(U&{q-3Xk(Hc>6UL~U4gRK~Lq?l&HTr0K*y)X-Ua7)oT zxZ2S@wu9&eN?Bl&-NhHa+He?ueOXY2K#s?iGh^bgwKXdq{4?Vb!A;wEz0v-iOXNe! zAe~y8l3$p5{pOGrwLwOO8aR;M3m}TguGslhmB0`G%>>oqWpL7JmR>ReCJ5sO8(12c z6I`b#Zz3gPmI~%&r_j*8I6ngRX(gg+3EVG09_m9X>;#RGV@L1g40hcqd*S7tea$D7 z>b#V9b{EMIU>hA;9?dy!qT^oRk|~z!+9If0mzov973*sGXY}(WA{aP^^LAw|BC24K z#wfFfK@Bh_p~3SFW~m*2Ge4?xpvjR^pGozQqjd)7Z_NEGvxa>n1=`QK;1mW# zA`j$%SzWh1)D9_O)Mi6f#+>52X%TBVCZ8ZQQt>T25B;(;Xg@#{Nr zD`eaO{uo6X{ik^!Nhy@|o*{-W_h`zVgA~6ypg-)>9KSgC?VJgLe!_W8bN}w-1WVK9 zZQF!|Vf1JfD0vF8&npN}^h#>$M1q>fwY%RrS!V}%^)78UQlm$nyxU55SV&7!&3Euf z3yNpz%%Rdm&Hl_ep^tgS7zQLVN92st%xp9$#Ogkg7ZjvJLqpQUqm=f<+VQSCyuR@| z55g1t<}8tK=f;zmD6gjrj!+o6z;4ADZJ+QnvcByj#1@Jmk-Dl_(YkS~($B|OfqJQl z>rC0)V{}`Ow$gbbYw%j@(0cc9L3G4)i1 znj)vgTAyMF=SZjm(j&?LHh7*;YO&(z2Z1>JkMKY_@NwWX`|Z4?*J8f`#tb}Es^C@{ z)d7}B7A*=imOo^Hx?XCZ49EtSZL?9w*!X{Dyr!E`H#{iM4?@QlJ8t`0ebVb8gz8#q zXF`VhV$Ev*`ej!ouT3l_R!&I$XGU%X^J9N+Q>siL1}+plWVff1omT?-7o<3JjiFFt zizDyL@f)TgQ1cDbpe2Xl3IeIWWAl1dnTc=T*kjtAi^9M;7y4qyRd|CRH z`YRMA0`xHM;N1FQ!_}`d3r-n6KRp>9+dg$udwX)Lroz{jm+$dZ#eyG6BvOav$n<2u zM|-CKeR7&Pzia3yjfP#AhOLF{y^x&|x$OxJf9p{{CRJXTt=Y~ELv)b)neRW!ih9|e z0!Wu*gi$)VjIs{aeigjW=gjL7YMOeCz8Q{^MQD(ZsFsd&1IM@eU!iVI6TUH@Wr^S= zAc=&hVQwmOK6M zwjWpGOWzLSb-df0f_g6sTQZ1lJ$}=raez`8!3yc2w5f0jM5o_y3q{&X4cF3d8cduA zv(5?yLgM)v$xFtzphJTEP)Ye?1Bvnw&8pXFIMn|fIn~Q$hm!t|xs({}B;-U6dso)sU{qOb1?70$=+w@g-;2lUI=D*U_DZN?#+^krM;m1jD8Ai8Q z4(LoD01vp4v@qzQs4M8FVDd66kZ?H!03%o#wxZA%G7L0lZd8{;s8@H8DV|9@Y?&6e zNRplShi_a_*`?hNy^JJO0Wfk03!PiM;(Vd= zfafGO?rZw>_!5bGs>cCJN`*ecUh;eI(V3S*()~|6qUuL_P4@4HT|jkw7JC;KwqG)~ zRS9K}cY*kzANB{`&t`ipf)2jd%iovXT)ZlRSGF@u#Kwz#PF1OnOkDFD$z!8FNC$|<54$$+(haNTG~Yr_4ePA7-mp7k_3X9(0Lg z{o|DY%9wnm-s>DI3pN6UsZ?urKV*JSM!$@ueZxhYoR4`#LJ@S^z zNlmGQQR$ApT(sWQ>ko6qo>OS6f*(FT)-vsZXKOZ#bzC~yF`N84KW|Mz*B$`e9Wu4By8FT=7i%1^ZNgv3e3#u z_AX$?51%wwW#;8TeR0W85thTZ{`ChzExw3FK7oO+k-=2z(|_8rUC;#lyn*tFWwL>2 z>wh6~e4UIS?G(eiQ3JYYuj*JA`OaAgdRYd~p~+iN(#f4uu;*Gf+AM^3i=1O*ptX)_ z=6QddBDAS|fVq6sfAOPs6{>RftzAmnmOYTJECeMkY|-4ee8R)$Til~wgqD_37(Lr; z=gc-0q;w6ZRabGF+e|aGr3PE0wRQ-F4kEAKZk91Xi}ugo$RG3J86n#f5$zS*s{fhs zt)2ENqzbP6L*lJBaw7?dNE}KxFockUetrTqJ*?c9zCZiwg`(}?n;@$D9+_^LKescE-2LD1+-v&aM zb7@)nT9KqpSKq(P`0396=PaJP_$d{i-|Z$Z|UP|Kc~oQ<>LCZM5wTR--$Jw*tAfjFG%aK|0U2I zT;c8);hscr_~RE`4$Daw)To6PghuvMk#6ppuJ{s#BgPsog=Co_v0m~P?;1(>0z2n9IeW*4i@8@8?`|8Jq5Fx3&a}Q z1A)55*5$Dvei!GE2{am4Lb>WDMH-K?vgv2x*t5X>7EyUgyj<*i*p!F)5xe})yQl1L z-DJO-=ykVRc8u+R5%vS?O4#u=*-jm9UwhR^cSgztZ1WW56;>%bp%hJY{^(>4Sz3koi7)$XD&=l+>V30WlyexK1)4x`y z#n8|zA1wtYNrctOO&$h#7Ekk*MPKWxH(V^-z$Jm){lG51nHmxkiRh?U90bMo)cp#4 zUhJ}~no$j2n<;rdE1y9KhZ-Pw?|yM(YN`y%3Mb4%K^aCf(fIqRFED%M z^uQ$Wd)^f$q%JD)@kAU><3}0{bLQBx!(1-E!;kv3J!6%6b{D!$|$5mc{#D6iBjg zQ@5plB$+6*;oYDetYBRYfP`cgSXp?ZwCtfw>DqeS zn`VC?S4TBZ5#XLq;_xR1p3vquEE9@>5$ZzdQ>*&9;I+0bFF!c#+@yP4VMj7y=^erK z>-&Q$OYuF}@r=c(yoHy7TyGZWJPieMF!t zGDOOsWlU0Rzs5Wr>pS#&PI+i-Ac1^G)`p#Vs@<~q;`2=MoXU`UcyNO)A>J|pst#Zg zjL{0}wOp8yj&ZNGqUcW*<>7R5py(^3ynw-quJJxfEo(uL#?dR~zR1^f`H;UKG_*-j zQ?BEF)ITQL+>gf1^f}lue(&*t4jPRXfyBPKoM2l!^mX=+TMrk69^o{**z^m>Nb=}` z*-*_=w~*Zo+iqv~(jT_I17<@Bx4oKBj~0{aNdF!dcloc9M_pw5LL&<@0pB)MpEndh zwy*6AFmxFT{9CFEBoCqCs?layJq!DD9mXedy8Nny?56kn* zT(&kUk6m}p@G7WV&`r(#A==1Xpg7l?b1Npfw@HJ0Xv$-jVk~}Jps>B#!sGm$qe>%F ztC#WM6SrIHTYY`D8_KuKjLmYPz76}aKR!8jW9V4sSGBuE-u9DzDA}&%ewWKS%8wDU zT7cjqW7`V}`(8vo&Q|9CY)(uha>AKe<(A3oHeI&P_@j8FUBq$uk-4tmrd`yPJ(an? zmY?c`L6Nu2%lKL51G%n$jw&zR_Y%n+VVCPnw(u>q<8p^4JWj~%56M1k${JA~grjqm zG0W2X#{pCJfwy~?`gwAnnxfbC9gcEIiKF(~RzLPZB@+|GXsp⁣^_!Q#Hj!+_83e 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 literal 0 HcmV?d00001 diff --git a/docs/user-guide/_images/freepik_research_vessel.jpg b/docs/user-guide/_images/freepik_research_vessel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71163e06b4116b571c9d61c47aeb9b6f5f12f09b GIT binary patch literal 353099 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

tSZ>*p797QIUB1Nrm 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�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 Date: Wed, 5 Nov 2025 12:53:49 +0100 Subject: [PATCH 44/56] move product id selection logic to utils --- src/virtualship/instruments/base.py | 128 +--------------------------- src/virtualship/utils.py | 124 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 125 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index a35aa81a..0f530702 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,49 +4,15 @@ from typing import TYPE_CHECKING import copernicusmarine -import numpy as np -from parcels import Field, FieldSet from yaspin import yaspin +from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.errors import CopernicusCatalogueError -from virtualship.utils import ship_spinner +from virtualship.utils import _select_product_id, ship_spinner if TYPE_CHECKING: from virtualship.models import Expedition, SpaceTimeRegion -PRODUCT_IDS = { - "phys": { - "reanalysis": "cmems_mod_glo_phy_my_0.083deg_P1D-m", - "reanalysis_interim": "cmems_mod_glo_phy_myint_0.083deg_P1D-m", - "analysis": "cmems_mod_glo_phy_anfc_0.083deg_P1D-m", - }, - "bgc": { - "reanalysis": "cmems_mod_glo_bgc_my_0.25deg_P1D-m", - "reanalysis_interim": "cmems_mod_glo_bgc_myint_0.25deg_P1D-m", - "analysis": None, # will be set per variable - }, -} - -BGC_ANALYSIS_IDS = { - "o2": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", - "chl": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "no3": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "po4": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", - "ph": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", - "phyc": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", - "nppv": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", -} - -MONTHLY_BGC_REANALYSIS_IDS = { - "ph": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", - "phyc": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", -} -MONTHLY_BGC_REANALYSIS_INTERIM_IDS = { - "ph": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", - "phyc": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", -} - class InputDataset(abc.ABC): """Base class for instrument input datasets.""" @@ -111,7 +77,7 @@ def download_data(self) -> None: else: variable = dataset.get("variables")[0] # BGC variables, special case - dataset_id = self._select_product_id( + dataset_id = _select_product_id( physical=physical, schedule_start=self.space_time_region.time_range.start_time, schedule_end=self.space_time_region.time_range.end_time, @@ -126,94 +92,6 @@ def download_data(self) -> None: } copernicusmarine.subset(**download_args) - def _select_product_id( - self, - physical: bool, - schedule_start, - schedule_end, - username: str, - password: str, - variable: str | None = None, - ) -> str: - """Determine which copernicus product id should be selected (reanalysis, reanalysis-interim, analysis & forecast), for prescribed schedule and physical vs. BGC.""" - key = "phys" if physical else "bgc" - selected_id = None - - for period, pid in PRODUCT_IDS[key].items(): - # for BGC analysis, set pid per variable - if key == "bgc" and period == "analysis": - if variable is None or variable not in BGC_ANALYSIS_IDS: - continue - pid = BGC_ANALYSIS_IDS[variable] - # for BGC reanalysis, check if requires monthly product - if ( - key == "bgc" - and period == "reanalysis" - and variable in MONTHLY_BGC_REANALYSIS_IDS - ): - monthly_pid = MONTHLY_BGC_REANALYSIS_IDS[variable] - ds_monthly = copernicusmarine.open_dataset( - monthly_pid, - username=username, - password=password, - ) - time_end_monthly = ds_monthly["time"][-1].values - if np.datetime64(schedule_end) <= time_end_monthly: - pid = monthly_pid - # for BGC reanalysis_interim, check if requires monthly product - if ( - key == "bgc" - and period == "reanalysis_interim" - and variable in MONTHLY_BGC_REANALYSIS_INTERIM_IDS - ): - monthly_pid = MONTHLY_BGC_REANALYSIS_INTERIM_IDS[variable] - ds_monthly = copernicusmarine.open_dataset( - monthly_pid, username=username, password=password - ) - time_end_monthly = ds_monthly["time"][-1].values - if np.datetime64(schedule_end) <= time_end_monthly: - pid = monthly_pid - if pid is None: - continue - ds = copernicusmarine.open_dataset( - pid, username=username, password=password - ) - time_end = ds["time"][-1].values - if np.datetime64(schedule_end) <= time_end: - selected_id = pid - break - - if selected_id is None: - raise CopernicusCatalogueError( - "No suitable product found in the Copernicus Marine Catalogue for the scheduled time and variable." - ) - - if self._start_end_in_product_timerange( - selected_id, schedule_start, schedule_end, username, password - ): - return selected_id - else: - return ( - PRODUCT_IDS["phys"]["analysis"] - if physical - else BGC_ANALYSIS_IDS[variable] - ) - - def _start_end_in_product_timerange( - self, selected_id, schedule_start, schedule_end, username, password - ): - ds_selected = copernicusmarine.open_dataset( - selected_id, username=username, password=password - ) - time_values = ds_selected["time"].values - import numpy as np - - time_min, time_max = np.min(time_values), np.max(time_values) - return ( - np.datetime64(schedule_start) >= time_min - and np.datetime64(schedule_end) <= time_max - ) - class Instrument(abc.ABC): """Base class for instruments and their simulation.""" diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 324f8c67..8f1fadd4 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -8,7 +8,11 @@ from pathlib import Path from typing import TYPE_CHECKING, TextIO +import copernicusmarine +import numpy as np + from parcels import FieldSet +from virtualship.errors import CopernicusCatalogueError if TYPE_CHECKING: from virtualship.models import Expedition @@ -293,3 +297,123 @@ def add_dummy_UV(fieldset: FieldSet): raise ValueError( "Cannot determine time_origin for dummy UV fields. Assert T or o2 exists in fieldset." ) from None + + +# Copernicus Marine product IDs + +PRODUCT_IDS = { + "phys": { + "reanalysis": "cmems_mod_glo_phy_my_0.083deg_P1D-m", + "reanalysis_interim": "cmems_mod_glo_phy_myint_0.083deg_P1D-m", + "analysis": "cmems_mod_glo_phy_anfc_0.083deg_P1D-m", + }, + "bgc": { + "reanalysis": "cmems_mod_glo_bgc_my_0.25deg_P1D-m", + "reanalysis_interim": "cmems_mod_glo_bgc_myint_0.25deg_P1D-m", + "analysis": None, # will be set per variable + }, +} + +BGC_ANALYSIS_IDS = { + "o2": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", + "chl": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "no3": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "po4": "cmems_mod_glo_bgc-nut_anfc_0.25deg_P1D-m", + "ph": "cmems_mod_glo_bgc-car_anfc_0.25deg_P1D-m", + "phyc": "cmems_mod_glo_bgc-pft_anfc_0.25deg_P1D-m", + "nppv": "cmems_mod_glo_bgc-bio_anfc_0.25deg_P1D-m", +} + +MONTHLY_BGC_REANALYSIS_IDS = { + "ph": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", + "phyc": "cmems_mod_glo_bgc_my_0.25deg_P1M-m", +} +MONTHLY_BGC_REANALYSIS_INTERIM_IDS = { + "ph": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", + "phyc": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", +} + + +def _select_product_id( + physical: bool, + schedule_start, + schedule_end, + username: str, + password: str, + variable: str | None = None, +) -> str: + """Determine which copernicus product id should be selected (reanalysis, reanalysis-interim, analysis & forecast), for prescribed schedule and physical vs. BGC.""" + key = "phys" if physical else "bgc" + selected_id = None + + for period, pid in PRODUCT_IDS[key].items(): + # for BGC analysis, set pid per variable + if key == "bgc" and period == "analysis": + if variable is None or variable not in BGC_ANALYSIS_IDS: + continue + pid = BGC_ANALYSIS_IDS[variable] + # for BGC reanalysis, check if requires monthly product + if ( + key == "bgc" + and period == "reanalysis" + and variable in MONTHLY_BGC_REANALYSIS_IDS + ): + monthly_pid = MONTHLY_BGC_REANALYSIS_IDS[variable] + ds_monthly = copernicusmarine.open_dataset( + monthly_pid, + username=username, + password=password, + ) + time_end_monthly = ds_monthly["time"][-1].values + if np.datetime64(schedule_end) <= time_end_monthly: + pid = monthly_pid + # for BGC reanalysis_interim, check if requires monthly product + if ( + key == "bgc" + and period == "reanalysis_interim" + and variable in MONTHLY_BGC_REANALYSIS_INTERIM_IDS + ): + monthly_pid = MONTHLY_BGC_REANALYSIS_INTERIM_IDS[variable] + ds_monthly = copernicusmarine.open_dataset( + monthly_pid, username=username, password=password + ) + time_end_monthly = ds_monthly["time"][-1].values + if np.datetime64(schedule_end) <= time_end_monthly: + pid = monthly_pid + if pid is None: + continue + ds = copernicusmarine.open_dataset(pid, username=username, password=password) + time_end = ds["time"][-1].values + if np.datetime64(schedule_end) <= time_end: + selected_id = pid + break + + if selected_id is None: + raise CopernicusCatalogueError( + "No suitable product found in the Copernicus Marine Catalogue for the scheduled time and variable." + ) + + if _start_end_in_product_timerange( + selected_id, schedule_start, schedule_end, username, password + ): + return selected_id + else: + return ( + PRODUCT_IDS["phys"]["analysis"] if physical else BGC_ANALYSIS_IDS[variable] + ) + + +def _start_end_in_product_timerange( + selected_id, schedule_start, schedule_end, username, password +): + ds_selected = copernicusmarine.open_dataset( + selected_id, username=username, password=password + ) + time_values = ds_selected["time"].values + import numpy as np + + time_min, time_max = np.min(time_values), np.max(time_values) + return ( + np.datetime64(schedule_start) >= time_min + and np.datetime64(schedule_end) <= time_max + ) From c447dd86c446e7a77e2aba2a87d6a6efdff9e47b Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:11:46 +0100 Subject: [PATCH 45/56] update --- src/virtualship/instruments/base.py | 69 +++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 0f530702..0e5ff37a 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -96,6 +96,11 @@ def download_data(self) -> None: class Instrument(abc.ABC): """Base class for instruments and their simulation.""" + #! TODO List: + # TODO: update documentation/quickstart + # TODO: update tests + # TODO: if use direct ingestion as primary data sourcing, can substantially cut code base (including _fetch.py, InputDataset objects). Consider this for Parcels v4 transition. + def __init__( self, name: str, @@ -107,6 +112,7 @@ def __init__( allow_time_extrapolation: bool, verbose_progress: bool, bathymetry_file: str = "bathymetry.nc", + direct: bool = False, ): """Initialise instrument.""" self.name = name @@ -124,27 +130,54 @@ def __init__( self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation self.verbose_progress = verbose_progress + self.direct = direct def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" - # TODO: tests need updating...! - - try: - data_dir = self._get_data_dir(self.directory) - joined_filepaths = { - key: data_dir.joinpath(filename) - for key, filename in self.filenames.items() - } - fieldset = FieldSet.from_netcdf( - joined_filepaths, - self.variables, - self.dimensions, - allow_time_extrapolation=self.allow_time_extrapolation, - ) - except FileNotFoundError as e: - raise FileNotFoundError( - f"Input data for instrument {self.name} not found. Have you run the `virtualship fetch` command??" - ) from e + if self.direct: # if direct ingestion from Copernicus Marine is enabled + try: + # ds = copernicusmarine.open_dataset( + # dataset_id="PHYS_REANALYSIS_ID", + # dataset_part="default", + # minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude, + # maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude, + # minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude, + # maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude, + # variables=["uo", "vo", "so", "thetao"], + # start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, + # end_datetime=self.expedition.schedule.space_time_region.time_range.end_time, + # coordinates_selection_method="outside", + # ) + + #! TODO: FIX! + fieldset = copernicusmarine.FieldSet.from_copernicus( + self.expedition.schedule.space_time_region, + self.variables, + self.dimensions, + allow_time_extrapolation=self.allow_time_extrapolation, + ) + except FileNotFoundError as e: + raise FileNotFoundError( + "ERROR" # TODO: improve error message! + ) from e + + else: # from fetched data on disk + try: + data_dir = self._get_data_dir(self.directory) + joined_filepaths = { + key: data_dir.joinpath(filename) + for key, filename in self.filenames.items() + } + fieldset = FieldSet.from_netcdf( + joined_filepaths, + self.variables, + self.dimensions, + allow_time_extrapolation=self.allow_time_extrapolation, + ) + except FileNotFoundError as e: + raise FileNotFoundError( + f"Input data for instrument {self.name} not found. Have you run the `virtualship fetch` command??" + ) from e # interpolation methods for var in (v for v in self.variables if v not in ("U", "V")): From 870271bc04626a61c0568efae53874d29a0e4bd1 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:53:31 +0100 Subject: [PATCH 46/56] first draft direct ingestion via copernicusmarine (CTD only) --- src/virtualship/cli/commands.py | 11 +++- src/virtualship/expedition/do_expedition.py | 25 +++++--- src/virtualship/instruments/base.py | 70 +++++++++++++-------- src/virtualship/instruments/ctd.py | 5 +- src/virtualship/models/expedition.py | 44 +++++++------ src/virtualship/utils.py | 24 ++++++- 6 files changed, 123 insertions(+), 56 deletions(-) diff --git a/src/virtualship/cli/commands.py b/src/virtualship/cli/commands.py index 3e83be3b..0f3c058a 100644 --- a/src/virtualship/cli/commands.py +++ b/src/virtualship/cli/commands.py @@ -106,11 +106,18 @@ def fetch(path: str | Path, username: str | None, password: str | None) -> None: _fetch(path, username, password) +# TODO: also add option to 'stream' via link to dir elsewhere, e.g. simlink or path to data stored elsewhere that isn't expedition dir! @click.command() @click.argument( "path", type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True), ) -def run(path): +@click.option( + "--from-copernicusmarine", + is_flag=True, + default=False, + help="Ingest fieldsets directly via copernicusmarine toolbox.", +) +def run(path, from_copernicusmarine: bool): """Run the expedition.""" - do_expedition(Path(path)) + do_expedition(Path(path), from_copernicusmarine) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index b857b72c..eaef2b34 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -31,12 +31,16 @@ external_logger = logging.getLogger("parcels.tools.loggers") external_logger.setLevel(logging.WARNING) +# copernicusmarine logger (suppress INFO messages to prevent log being flooded) +logging.getLogger("copernicusmarine").setLevel("ERROR") -def do_expedition(expedition_dir: str | Path) -> None: + +def do_expedition(expedition_dir: str | Path, from_copernicusmarine: bool) -> None: """ Perform an expedition, providing terminal feedback and file output. :param expedition_dir: The base directory for the expedition. + :param from_copernicusmarine: Whether to use direct data ingestion from Copernicus Marine. Should be determined by CLI flag. """ print("\n╔═════════════════════════════════════════════════╗") print("║ VIRTUALSHIP EXPEDITION STATUS ║") @@ -61,12 +65,15 @@ def do_expedition(expedition_dir: str | Path) -> None: print("\n---- WAYPOINT VERIFICATION ----") # verify schedule is valid - data_dir = get_existing_download( - expedition_dir, - get_space_time_region_hash(expedition.schedule.space_time_region), - ) + if from_copernicusmarine: + bathy_data_dir = None + else: + bathy_data_dir = get_existing_download( + expedition_dir, + get_space_time_region_hash(expedition.schedule.space_time_region), + ) - expedition.schedule.verify(expedition.ship_config.ship_speed_knots, data_dir) + expedition.schedule.verify(expedition.ship_config.ship_speed_knots, bathy_data_dir) # simulate the schedule schedule_results = simulate_schedule( @@ -117,7 +124,11 @@ def do_expedition(expedition_dir: str | Path) -> None: measurements = getattr(schedule_results.measurements_to_simulate, attr) # initialise instrument - instrument = instrument_class(expedition=expedition, directory=expedition_dir) + instrument = instrument_class( + expedition=expedition, + directory=expedition_dir, + from_copernicusmarine=from_copernicusmarine, + ) # run simulation instrument.run( diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 0e5ff37a..920b8dbf 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING import copernicusmarine +import xarray as xr from yaspin import yaspin from parcels import Field, FieldSet @@ -112,7 +113,7 @@ def __init__( allow_time_extrapolation: bool, verbose_progress: bool, bathymetry_file: str = "bathymetry.nc", - direct: bool = False, + from_copernicusmarine: bool = False, ): """Initialise instrument.""" self.name = name @@ -130,35 +131,31 @@ def __init__( self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation self.verbose_progress = verbose_progress - self.direct = direct + self.from_copernicusmarine = from_copernicusmarine def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" - if self.direct: # if direct ingestion from Copernicus Marine is enabled + if self.from_copernicusmarine: try: - # ds = copernicusmarine.open_dataset( - # dataset_id="PHYS_REANALYSIS_ID", - # dataset_part="default", - # minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude, - # maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude, - # minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude, - # maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude, - # variables=["uo", "vo", "so", "thetao"], - # start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, - # end_datetime=self.expedition.schedule.space_time_region.time_range.end_time, - # coordinates_selection_method="outside", - # ) - - #! TODO: FIX! - fieldset = copernicusmarine.FieldSet.from_copernicus( - self.expedition.schedule.space_time_region, - self.variables, - self.dimensions, - allow_time_extrapolation=self.allow_time_extrapolation, + datasets = [] + for var in self.variables.values(): + physical = ( + True if var in ("uo", "vo", "so", "thetao") else False + ) # TODO: add more if start using new physical variables! Or more dynamic way of determining? + ds = self._get_copernicus_ds( + physical=physical, var=var + ) # user should be prompted for credentials + datasets.append(ds) + + ds_concat = xr.merge(datasets) + fieldset = FieldSet.from_xarray_dataset( + ds_concat, self.variables, self.dimensions, mesh="spherical" ) - except FileNotFoundError as e: + + except Exception as e: raise FileNotFoundError( - "ERROR" # TODO: improve error message! + f"Failed to load input data directly from Copernicus Marine for instrument '{self.name}'. " + f"Please check your credentials, network connection, and variable names. Original error: {e}" ) from e else: # from fetched data on disk @@ -176,7 +173,8 @@ def load_input_data(self) -> FieldSet: ) except FileNotFoundError as e: raise FileNotFoundError( - f"Input data for instrument {self.name} not found. Have you run the `virtualship fetch` command??" + f"Input data for instrument {self.name} not found locally. Have you run the `virtualship fetch` command?" + "Alternatively, you can use the `--from-copernicusmarine` option to ingest data directly from Copernicus Marine." ) from e # interpolation methods @@ -230,3 +228,25 @@ def _get_data_dir(self, expedition_dir: Path) -> Path: ) return data_dir + + def _get_copernicus_ds(self, physical: bool, var: str) -> xr.Dataset: + """Get Copernicus Marine dataset for direct ingestion.""" + product_id = _select_product_id( + physical=physical, + schedule_start=self.expedition.schedule.space_time_region.time_range.start_time, + schedule_end=self.expedition.schedule.space_time_region.time_range.end_time, + variable=var if not physical else None, + ) + + return copernicusmarine.open_dataset( + dataset_id=product_id, + dataset_part="default", + minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude, + maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude, + minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude, + maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude, + variables=["uo", "vo", "so", "thetao"], + start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, + end_datetime=self.expedition.schedule.space_time_region.time_range.end_time, + coordinates_selection_method="outside", + ) diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 8ddb2822..7434da2d 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -3,8 +3,8 @@ from typing import TYPE_CHECKING, ClassVar import numpy as np -from parcels import JITParticle, ParticleSet, Variable +from parcels import JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType @@ -122,7 +122,7 @@ def get_datasets_dict(self) -> dict: class CTDInstrument(Instrument): """CTD instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize CTDInstrument.""" filenames = { "S": f"{CTD.name}_s.nc", @@ -139,6 +139,7 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index af85c1c5..14d9537d 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -12,7 +12,7 @@ from parcels import Field from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.types import InstrumentType -from virtualship.utils import _validate_numeric_mins_to_timedelta +from virtualship.utils import _get_bathy_data, _validate_numeric_mins_to_timedelta from .location import Location from .space_time_region import SpaceTimeRegion @@ -89,7 +89,7 @@ class Schedule(pydantic.BaseModel): def verify( self, ship_speed: float, - data_dir: str | Path | None, + bathy_data_dir: str | Path | None, ignore_missing_bathymetry: bool = False, *, check_space_time_region: bool = False, @@ -132,18 +132,30 @@ def verify( # check if all waypoints are in water using bathymetry data # TODO: write test that checks that will flag when waypoint is on land!! [add to existing suite of fail .verify() tests in test_expedition.py] land_waypoints = [] - if data_dir is not None: - bathymetry_path = data_dir.joinpath("bathymetry.nc") - try: - bathymetry_field = Field.from_netcdf( - bathymetry_path, - variable=("bathymetry", "deptho"), - dimensions={"lon": "longitude", "lat": "latitude"}, - ) - except Exception as e: - raise ScheduleError( - f"Problem loading bathymetry data (used to verify waypoints are in water): {e}" - ) from e + if not ignore_missing_bathymetry: + if bathy_data_dir is None: + try: + bathymetry_field = _get_bathy_data( + self.space_time_region + ).bathymetry # via copernicusmarine + except Exception as e: + raise ScheduleError( + f"Problem loading bathymetry data (used to verify waypoints are in water) directly via copernicusmarine. \n\n original message: {e}" + ) from e + + else: + bathymetry_path = bathy_data_dir.joinpath("bathymetry.nc") + try: + bathymetry_field = Field.from_netcdf( + bathymetry_path, + variable=("bathymetry", "deptho"), + dimensions={"lon": "longitude", "lat": "latitude"}, + ) + except Exception as e: + raise ScheduleError( + f"Problem loading local bathymetry data (used to verify waypoints are in water). Have you run `virtualship fetch` command?. \n\n original message: {e}" + ) from e + for wp_i, wp in enumerate(self.waypoints): try: bathymetry_field.eval( @@ -159,10 +171,6 @@ def verify( raise ScheduleError( f"The following waypoint(s) throw(s) error(s): {['#' + str(wp_i + 1) + ' ' + str(wp) for (wp_i, wp) in land_waypoints]}\n\nINFO: They are likely on land (bathymetry data cannot be interpolated to their location(s)).\n" ) - elif not ignore_missing_bathymetry: - raise ScheduleError( - "Cannot verify waypoints are in water as bathymetry data not found. Have you run `virtualship fetch` command?" - ) # check that ship will arrive on time at each waypoint (in case no unexpected event happen) time = self.waypoints[0].time diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 8f1fadd4..a013d37e 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -338,8 +338,8 @@ def _select_product_id( physical: bool, schedule_start, schedule_end, - username: str, - password: str, + username: str | None = None, + password: str | None = None, variable: str | None = None, ) -> str: """Determine which copernicus product id should be selected (reanalysis, reanalysis-interim, analysis & forecast), for prescribed schedule and physical vs. BGC.""" @@ -417,3 +417,23 @@ def _start_end_in_product_timerange( np.datetime64(schedule_start) >= time_min and np.datetime64(schedule_end) <= time_max ) + + +def _get_bathy_data(space_time_region) -> FieldSet: + """Bathymetry data 'streamed' directly from Copernicus Marine.""" + ds_bathymetry = copernicusmarine.open_dataset( + dataset_id="cmems_mod_glo_phy_my_0.083deg_static", + minimum_longitude=space_time_region.spatial_range.minimum_longitude, + maximum_longitude=space_time_region.spatial_range.maximum_longitude, + minimum_latitude=space_time_region.spatial_range.minimum_latitude, + maximum_latitude=space_time_region.spatial_range.maximum_latitude, + variables=["deptho"], + start_datetime=space_time_region.time_range.start_time, + end_datetime=space_time_region.time_range.end_time, + coordinates_selection_method="outside", + ) + bathymetry_variables = {"bathymetry": "deptho"} + bathymetry_dimensions = {"lon": "longitude", "lat": "latitude"} + return FieldSet.from_xarray_dataset( + ds_bathymetry, bathymetry_variables, bathymetry_dimensions + ) From 701e45d5005ee50cc8bad166ae92f1e222d87cca Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:23:57 +0100 Subject: [PATCH 47/56] refactor bathymetry data handling and add (temporary) timing for performance evaluation --- src/virtualship/cli/_plan.py | 2 +- src/virtualship/expedition/do_expedition.py | 13 +++++++++++ src/virtualship/instruments/base.py | 24 ++++++++++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/virtualship/cli/_plan.py b/src/virtualship/cli/_plan.py index c27cb741..6aa6ff28 100644 --- a/src/virtualship/cli/_plan.py +++ b/src/virtualship/cli/_plan.py @@ -1046,7 +1046,7 @@ def save_pressed(self) -> None: # verify schedule expedition_editor.expedition.schedule.verify( ship_speed_value, - data_dir=None, + bathy_data_dir=None, check_space_time_region=True, ignore_missing_bathymetry=True, ) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/expedition/do_expedition.py index eaef2b34..af9fc4aa 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/expedition/do_expedition.py @@ -42,6 +42,13 @@ def do_expedition(expedition_dir: str | Path, from_copernicusmarine: bool) -> No :param expedition_dir: The base directory for the expedition. :param from_copernicusmarine: Whether to use direct data ingestion from Copernicus Marine. Should be determined by CLI flag. """ + # ################################# TEMPORARY TIMER: START ################################# + import time + + start_time = time.time() + print("[TIMER] Expedition started...") + # ################################# TEMPORARY TIMER: START ################################# + print("\n╔═════════════════════════════════════════════════╗") print("║ VIRTUALSHIP EXPEDITION STATUS ║") print("╚═════════════════════════════════════════════════╝") @@ -145,6 +152,12 @@ def do_expedition(expedition_dir: str | Path, from_copernicusmarine: bool) -> No ) print("\n------------- END -------------\n") + ################################# TEMPORARY TIMER: END ################################# + end_time = time.time() + elapsed = end_time - start_time + print(f"[TIMER] Expedition completed in {elapsed:.2f} seconds.") + ################################# TEMPORARY TIMER: END ################################# + def _load_checkpoint(expedition_dir: Path) -> Checkpoint | None: file_path = expedition_dir.joinpath(CHECKPOINT) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 920b8dbf..ed40e151 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -9,7 +9,7 @@ from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.utils import _select_product_id, ship_spinner +from virtualship.utils import _get_bathy_data, _select_product_id, ship_spinner if TYPE_CHECKING: from virtualship.models import Expedition, SpaceTimeRegion @@ -147,7 +147,7 @@ def load_input_data(self) -> FieldSet: ) # user should be prompted for credentials datasets.append(ds) - ds_concat = xr.merge(datasets) + ds_concat = xr.merge(datasets) # TODO: deal with WARNINGS? fieldset = FieldSet.from_xarray_dataset( ds_concat, self.variables, self.dimensions, mesh="spherical" ) @@ -183,16 +183,24 @@ def load_input_data(self) -> FieldSet: # depth negative for g in fieldset.gridset.grids: g.negate_depth() + # bathymetry data if self.add_bathymetry: - bathymetry_field = Field.from_netcdf( - data_dir.joinpath(self.bathymetry_file), - variable=("bathymetry", "deptho"), - dimensions={"lon": "longitude", "lat": "latitude"}, - ) + if self.from_copernicusmarine: + bathymetry_field = _get_bathy_data( + self.expedition.schedule.space_time_region + ).bathymetry + else: + bathymetry_field = Field.from_netcdf( + data_dir.joinpath(self.bathymetry_file), + variable=("bathymetry", "deptho"), + dimensions={"lon": "longitude", "lat": "latitude"}, + ) bathymetry_field.data = -bathymetry_field.data fieldset.add_field(bathymetry_field) - fieldset.computeTimeChunk(0, 1) # read in data already + + # TODO: is this line necessary?! + # fieldset.computeTimeChunk(0, 1) # read in data already return fieldset From 81941155258ca4b068bcf45eaebabf9096366a52 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 6 Nov 2025 08:58:28 +0100 Subject: [PATCH 48/56] update instrument constructors for Copernicus Marine ingestion --- src/virtualship/cli/_fetch.py | 15 ++++++++++++++- src/virtualship/instruments/adcp.py | 5 +++-- src/virtualship/instruments/argo_float.py | 5 +++-- src/virtualship/instruments/base.py | 1 + src/virtualship/instruments/ctd_bgc.py | 5 +++-- src/virtualship/instruments/drifter.py | 5 +++-- src/virtualship/instruments/ship_underwater_st.py | 5 +++-- src/virtualship/instruments/xbt.py | 5 +++-- 8 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py index a41687d0..9722fe36 100644 --- a/src/virtualship/cli/_fetch.py +++ b/src/virtualship/cli/_fetch.py @@ -39,6 +39,13 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None be provided on prompt, via command line arguments, or via a YAML config file. Run `virtualship fetch` on an expedition for more info. """ + # ################################# TEMPORARY TIMER: START ################################# + import time + + start_time = time.time() + print("[TIMER] Expedition started...") + # ################################# TEMPORARY TIMER: START ################################# + if sum([username is None, password is None]) == 1: raise ValueError("Both username and password must be provided when using CLI.") @@ -51,7 +58,7 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None expedition.schedule.verify( expedition.ship_config.ship_speed_knots, - data_dir=None, + bathy_data_dir=None, check_space_time_region=True, ignore_missing_bathymetry=True, ) @@ -129,6 +136,12 @@ def _fetch(path: str | Path, username: str | None, password: str | None) -> None complete_download(download_folder) + ################################# TEMPORARY TIMER: END ################################# + end_time = time.time() + elapsed = end_time - start_time + print(f"[TIMER] Expedition completed in {elapsed:.2f} seconds.") + ################################# TEMPORARY TIMER: END ################################# + def _hash(s: str, *, length: int) -> str: """Create a hash of a string.""" diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 857e9655..2702cbfd 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np -from parcels import ParticleSet, ScipyParticle, Variable +from parcels import ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import ( @@ -95,7 +95,7 @@ def get_datasets_dict(self) -> dict: class ADCPInstrument(Instrument): """ADCP instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize ADCPInstrument.""" filenames = { "U": f"{ADCP.name}_uv.nc", @@ -111,6 +111,7 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 64961605..30def124 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -4,6 +4,7 @@ from typing import ClassVar import numpy as np + from parcels import ( AdvectionRK4, JITParticle, @@ -11,7 +12,6 @@ StatusCode, Variable, ) - from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime @@ -187,7 +187,7 @@ def get_datasets_dict(self) -> dict: class ArgoFloatInstrument(Instrument): """ArgoFloat instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize ArgoFloatInstrument.""" filenames = { "U": f"{ArgoFloat.name}_uv.nc", @@ -205,6 +205,7 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index ed40e151..a0e34e42 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -101,6 +101,7 @@ class Instrument(abc.ABC): # TODO: update documentation/quickstart # TODO: update tests # TODO: if use direct ingestion as primary data sourcing, can substantially cut code base (including _fetch.py, InputDataset objects). Consider this for Parcels v4 transition. + #! TODO: how is this handling credentials?! Seems to work already, are these set up from my previous instances of using copernicusmarine? Therefore users will only have to do it once too? def __init__( self, diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 23c978f7..89173bbf 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np -from parcels import JITParticle, ParticleSet, Variable +from parcels import JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime @@ -169,7 +169,7 @@ def get_datasets_dict(self) -> dict: class CTD_BGCInstrument(Instrument): """CTD_BGC instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize CTD_BGCInstrument.""" filenames = { "o2": f"{CTD_BGC.name}_o2.nc", @@ -198,6 +198,7 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index f854d51f..182100c2 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np -from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable +from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime @@ -108,7 +108,7 @@ def get_datasets_dict(self) -> dict: class DrifterInstrument(Instrument): """Drifter instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize DrifterInstrument.""" filenames = { "U": f"{Drifter.name}_uv.nc", @@ -125,6 +125,7 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 161bb184..5f793d3d 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np -from parcels import ParticleSet, ScipyParticle, Variable +from parcels import ParticleSet, ScipyParticle, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument @@ -100,7 +100,7 @@ def get_datasets_dict(self) -> dict: class Underwater_STInstrument(Instrument): """Underwater_ST instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize Underwater_STInstrument.""" filenames = { "S": f"{Underwater_ST.name}_s.nc", @@ -117,6 +117,7 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 68502533..ab11ed67 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np -from parcels import JITParticle, ParticleSet, Variable +from parcels import JITParticle, ParticleSet, Variable from virtualship.instruments.base import InputDataset, Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime @@ -127,7 +127,7 @@ def get_datasets_dict(self) -> dict: class XBTInstrument(Instrument): """XBT instrument class.""" - def __init__(self, expedition, directory): + def __init__(self, expedition, directory, from_copernicusmarine): """Initialize XBTInstrument.""" filenames = { "U": f"{XBT.name}_uv.nc", @@ -145,6 +145,7 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: From 446b8d0b503a395c99806f0e554501ebf3c80e72 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:16:47 +0100 Subject: [PATCH 49/56] move expedition/do_expedition.py to cli/_run.py, rename Instrument.run() to Instrument.execute() --- .../do_expedition.py => cli/_run.py} | 21 +++++++++---------- src/virtualship/cli/commands.py | 4 ++-- src/virtualship/instruments/base.py | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) rename src/virtualship/{expedition/do_expedition.py => cli/_run.py} (94%) diff --git a/src/virtualship/expedition/do_expedition.py b/src/virtualship/cli/_run.py similarity index 94% rename from src/virtualship/expedition/do_expedition.py rename to src/virtualship/cli/_run.py index b857b72c..fb67ba5d 100644 --- a/src/virtualship/expedition/do_expedition.py +++ b/src/virtualship/cli/_run.py @@ -8,6 +8,13 @@ import pyproj from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash +from virtualship.expedition.checkpoint import Checkpoint +from virtualship.expedition.expedition_cost import expedition_cost +from virtualship.expedition.simulate_schedule import ( + MeasurementsToSimulate, + ScheduleProblem, + simulate_schedule, +) from virtualship.models import Schedule from virtualship.utils import ( CHECKPOINT, @@ -15,14 +22,6 @@ get_instrument_class, ) -from .checkpoint import Checkpoint -from .expedition_cost import expedition_cost -from .simulate_schedule import ( - MeasurementsToSimulate, - ScheduleProblem, - simulate_schedule, -) - # projection used to sail between waypoints projection = pyproj.Geod(ellps="WGS84") @@ -32,7 +31,7 @@ external_logger.setLevel(logging.WARNING) -def do_expedition(expedition_dir: str | Path) -> None: +def _run(expedition_dir: str | Path) -> None: """ Perform an expedition, providing terminal feedback and file output. @@ -119,8 +118,8 @@ def do_expedition(expedition_dir: str | Path) -> None: # initialise instrument instrument = instrument_class(expedition=expedition, directory=expedition_dir) - # run simulation - instrument.run( + # execute simulation + instrument.execute( measurements=measurements, out_path=expedition_dir.joinpath("results", f"{itype.name.lower()}.zarr"), ) diff --git a/src/virtualship/cli/commands.py b/src/virtualship/cli/commands.py index 3e83be3b..4571174d 100644 --- a/src/virtualship/cli/commands.py +++ b/src/virtualship/cli/commands.py @@ -5,7 +5,7 @@ from virtualship import utils from virtualship.cli._fetch import _fetch from virtualship.cli._plan import _plan -from virtualship.expedition.do_expedition import do_expedition +from virtualship.cli._run import _run from virtualship.utils import ( EXPEDITION, mfp_to_yaml, @@ -113,4 +113,4 @@ def fetch(path: str | Path, username: str | None, password: str | None) -> None: ) def run(path): """Run the expedition.""" - do_expedition(Path(path)) + _run(Path(path)) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 0f530702..4497764c 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -169,7 +169,7 @@ def load_input_data(self) -> FieldSet: def simulate(self, data_dir: Path, measurements: list, out_path: str | Path): """Simulate instrument measurements.""" - def run(self, measurements: list, out_path: str | Path) -> None: + def execute(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! From c97d31937a76c178ae8314c3e5ac2c367a4a3f3f Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:40:00 +0100 Subject: [PATCH 50/56] move checkpoint class to models, move expedition_cost() to utils.py --- src/virtualship/cli/_run.py | 2 +- src/virtualship/expedition/expedition_cost.py | 27 ------------------- .../{expedition => models}/checkpoint.py | 0 src/virtualship/utils.py | 24 +++++++++++++++++ 4 files changed, 25 insertions(+), 28 deletions(-) delete mode 100644 src/virtualship/expedition/expedition_cost.py rename src/virtualship/{expedition => models}/checkpoint.py (100%) diff --git a/src/virtualship/cli/_run.py b/src/virtualship/cli/_run.py index fb67ba5d..46815166 100644 --- a/src/virtualship/cli/_run.py +++ b/src/virtualship/cli/_run.py @@ -8,7 +8,6 @@ import pyproj from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.expedition.checkpoint import Checkpoint from virtualship.expedition.expedition_cost import expedition_cost from virtualship.expedition.simulate_schedule import ( MeasurementsToSimulate, @@ -16,6 +15,7 @@ simulate_schedule, ) from virtualship.models import Schedule +from virtualship.models.checkpoint import Checkpoint from virtualship.utils import ( CHECKPOINT, _get_expedition, diff --git a/src/virtualship/expedition/expedition_cost.py b/src/virtualship/expedition/expedition_cost.py deleted file mode 100644 index cab6ab7d..00000000 --- a/src/virtualship/expedition/expedition_cost.py +++ /dev/null @@ -1,27 +0,0 @@ -"""expedition_cost function.""" - -from datetime import timedelta - -from .simulate_schedule import ScheduleOk - - -def expedition_cost(schedule_results: ScheduleOk, time_past: timedelta) -> float: - """ - Calculate the cost of the expedition in US$. - - :param schedule_results: Results from schedule simulation. - :param time_past: Time the expedition took. - :returns: The calculated cost of the expedition in US$. - """ - SHIP_COST_PER_DAY = 30000 - DRIFTER_DEPLOY_COST = 2500 - ARGO_DEPLOY_COST = 15000 - - ship_cost = SHIP_COST_PER_DAY / 24 * time_past.total_seconds() // 3600 - num_argos = len(schedule_results.measurements_to_simulate.argo_floats) - argo_cost = num_argos * ARGO_DEPLOY_COST - num_drifters = len(schedule_results.measurements_to_simulate.drifters) - drifter_cost = num_drifters * DRIFTER_DEPLOY_COST - - cost = ship_cost + argo_cost + drifter_cost - return cost diff --git a/src/virtualship/expedition/checkpoint.py b/src/virtualship/models/checkpoint.py similarity index 100% rename from src/virtualship/expedition/checkpoint.py rename to src/virtualship/models/checkpoint.py diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 8f1fadd4..af98cd3d 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -15,8 +15,10 @@ from virtualship.errors import CopernicusCatalogueError if TYPE_CHECKING: + from virtualship.expedition.simulate_schedule import ScheduleOk from virtualship.models import Expedition + import pandas as pd import yaml from pydantic import BaseModel @@ -417,3 +419,25 @@ def _start_end_in_product_timerange( np.datetime64(schedule_start) >= time_min and np.datetime64(schedule_end) <= time_max ) + + +def expedition_cost(schedule_results: ScheduleOk, time_past: timedelta) -> float: + """ + Calculate the cost of the expedition in US$. + + :param schedule_results: Results from schedule simulation. + :param time_past: Time the expedition took. + :returns: The calculated cost of the expedition in US$. + """ + SHIP_COST_PER_DAY = 30000 + DRIFTER_DEPLOY_COST = 2500 + ARGO_DEPLOY_COST = 15000 + + ship_cost = SHIP_COST_PER_DAY / 24 * time_past.total_seconds() // 3600 + num_argos = len(schedule_results.measurements_to_simulate.argo_floats) + argo_cost = num_argos * ARGO_DEPLOY_COST + num_drifters = len(schedule_results.measurements_to_simulate.drifters) + drifter_cost = num_drifters * DRIFTER_DEPLOY_COST + + cost = ship_cost + argo_cost + drifter_cost + return cost From d1acdef6d88cf91f3842e47d6022256efe50dce3 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:48:40 +0100 Subject: [PATCH 51/56] update imports for expedition_cost --- src/virtualship/cli/_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtualship/cli/_run.py b/src/virtualship/cli/_run.py index 46815166..d364dae5 100644 --- a/src/virtualship/cli/_run.py +++ b/src/virtualship/cli/_run.py @@ -8,7 +8,6 @@ import pyproj from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.expedition.expedition_cost import expedition_cost from virtualship.expedition.simulate_schedule import ( MeasurementsToSimulate, ScheduleProblem, @@ -19,6 +18,7 @@ from virtualship.utils import ( CHECKPOINT, _get_expedition, + expedition_cost, get_instrument_class, ) From d7531ae416131a3e1bba0a9cf6ac9b6e75943658 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:14:10 +0100 Subject: [PATCH 52/56] working with drifters (bodge), CTD_BGC not yet working --- src/virtualship/instruments/base.py | 97 ++++++++++++++++++++++---- src/virtualship/instruments/ctd_bgc.py | 2 + src/virtualship/utils.py | 4 ++ 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index a0e34e42..9f3e9f53 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -4,12 +4,19 @@ from typing import TYPE_CHECKING import copernicusmarine +import numpy as np import xarray as xr from yaspin import yaspin from parcels import Field, FieldSet from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash -from virtualship.utils import _get_bathy_data, _select_product_id, ship_spinner +from virtualship.utils import ( + COPERNICUSMARINE_BGC_VARIABLES, + COPERNICUSMARINE_PHYS_VARIABLES, + _get_bathy_data, + _select_product_id, + ship_spinner, +) if TYPE_CHECKING: from virtualship.models import Expedition, SpaceTimeRegion @@ -140,15 +147,25 @@ def load_input_data(self) -> FieldSet: try: datasets = [] for var in self.variables.values(): - physical = ( - True if var in ("uo", "vo", "so", "thetao") else False - ) # TODO: add more if start using new physical variables! Or more dynamic way of determining? - ds = self._get_copernicus_ds( - physical=physical, var=var - ) # user should be prompted for credentials + physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False + + # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! + if self.name == "Drifter": + ds = self._get_copernicus_ds_DRIFTER(physical=physical, var=var) + else: + ds = self._get_copernicus_ds(physical=physical, var=var) datasets.append(ds) + # make sure time dims are matched if BGC variables are present (different monthly/daily resolutions can impact fieldset_endtime in simulate) + if any( + key in COPERNICUSMARINE_BGC_VARIABLES + for key in ds.keys() + for ds in datasets + ): + datasets = self._align_temporal(datasets) + ds_concat = xr.merge(datasets) # TODO: deal with WARNINGS? + fieldset = FieldSet.from_xarray_dataset( ds_concat, self.variables, self.dimensions, mesh="spherical" ) @@ -200,9 +217,6 @@ def load_input_data(self) -> FieldSet: bathymetry_field.data = -bathymetry_field.data fieldset.add_field(bathymetry_field) - # TODO: is this line necessary?! - # fieldset.computeTimeChunk(0, 1) # read in data already - return fieldset @abc.abstractmethod @@ -211,8 +225,6 @@ def simulate(self, data_dir: Path, measurements: list, out_path: str | Path): def run(self, measurements: list, out_path: str | Path) -> None: """Run instrument simulation.""" - # TODO: this will have to be able to handle the non-spinner/instead progress bar for drifters and argos! - if not self.verbose_progress: with yaspin( text=f"Simulating {self.name} measurements... ", @@ -226,6 +238,8 @@ def run(self, measurements: list, out_path: str | Path) -> None: self.simulate(measurements, out_path) print("\n") + # self.simulate(measurements, out_path) + def _get_data_dir(self, expedition_dir: Path) -> Path: space_time_region_hash = get_space_time_region_hash( self.expedition.schedule.space_time_region @@ -238,7 +252,11 @@ def _get_data_dir(self, expedition_dir: Path) -> Path: return data_dir - def _get_copernicus_ds(self, physical: bool, var: str) -> xr.Dataset: + def _get_copernicus_ds( + self, + physical: bool, + var: str, + ) -> xr.Dataset: """Get Copernicus Marine dataset for direct ingestion.""" product_id = _select_product_id( physical=physical, @@ -254,8 +272,59 @@ def _get_copernicus_ds(self, physical: bool, var: str) -> xr.Dataset: maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude, minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude, maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude, - variables=["uo", "vo", "so", "thetao"], + variables=[var], start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, end_datetime=self.expedition.schedule.space_time_region.time_range.end_time, coordinates_selection_method="outside", ) + + # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! + def _get_copernicus_ds_DRIFTER( + self, + physical: bool, + var: str, + ) -> xr.Dataset: + """Get Copernicus Marine dataset for direct ingestion.""" + product_id = _select_product_id( + physical=physical, + schedule_start=self.expedition.schedule.space_time_region.time_range.start_time, + schedule_end=self.expedition.schedule.space_time_region.time_range.end_time, + variable=var if not physical else None, + ) + + return copernicusmarine.open_dataset( + dataset_id=product_id, + dataset_part="default", + minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude + - 3.0, + maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude + + 3.0, + minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude + - 3.0, + maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude + + 3.0, + maximum_depth=1.0, + minimum_depth=1.0, + variables=[var], + start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, + end_datetime=self.expedition.schedule.space_time_region.time_range.end_time + + timedelta(days=21.0), + coordinates_selection_method="outside", + ) + + def _align_temporal(self, datasets: list[xr.Dataset]) -> list[xr.Dataset]: + """Align monthly and daily time dims of multiple datasets (by repeating monthly values daily).""" + reference_time = datasets[ + np.argmax(ds.time for ds in datasets) + ].time # daily timeseries + + datasets_aligned = [] + for ds in datasets: + if not np.array_equal(ds.time, reference_time): + # TODO: NEED TO CHOOSE BEST METHOD HERE + # ds = ds.resample(time="1D").ffill().reindex(time=reference_time) + # ds = ds.resample(time="1D").ffill() + ds = ds.reindex({"time": reference_time}, method="nearest") + datasets_aligned.append(ds) + + return datasets_aligned diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 89173bbf..505e4bcc 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -272,6 +272,8 @@ def simulate(self, measurements, out_path) -> None: # define output file for the simulation out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=OUTPUT_DT) + breakpoint() + # execute simulation ctd_bgc_particleset.execute( [ diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index a013d37e..19a6d30b 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -333,6 +333,10 @@ def add_dummy_UV(fieldset: FieldSet): "phyc": "cmems_mod_glo_bgc_myint_0.25deg_P1M-m", } +# variables used in VirtualShip which are physical or biogeochemical variables, respectively +COPERNICUSMARINE_PHYS_VARIABLES = ["uo", "vo", "so", "thetao"] +COPERNICUSMARINE_BGC_VARIABLES = ["o2", "chl", "no3", "po4", "ph", "phyc", "nppv"] + def _select_product_id( physical: bool, From 118e685f60c0e7e8f9d42894953ce1ec707b1be8 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:18:42 +0100 Subject: [PATCH 53/56] remove fetch and all associated logic --- src/virtualship/cli/_creds.py | 5 + src/virtualship/cli/_fetch.py | 240 ------------------ src/virtualship/cli/_run.py | 16 +- src/virtualship/cli/commands.py | 41 +-- src/virtualship/cli/main.py | 1 - src/virtualship/instruments/adcp.py | 46 +--- src/virtualship/instruments/argo_float.py | 57 +---- src/virtualship/instruments/base.py | 197 +++----------- src/virtualship/instruments/ctd.py | 50 +--- src/virtualship/instruments/ctd_bgc.py | 75 +----- src/virtualship/instruments/drifter.py | 52 +--- .../instruments/ship_underwater_st.py | 52 +--- src/virtualship/instruments/xbt.py | 57 +---- src/virtualship/models/expedition.py | 35 +-- src/virtualship/utils.py | 15 +- 15 files changed, 76 insertions(+), 863 deletions(-) delete mode 100644 src/virtualship/cli/_fetch.py diff --git a/src/virtualship/cli/_creds.py b/src/virtualship/cli/_creds.py index 9f1d2435..d9169ec4 100644 --- a/src/virtualship/cli/_creds.py +++ b/src/virtualship/cli/_creds.py @@ -1,3 +1,8 @@ +# + + +# TODO: TO DELETE?! + from __future__ import annotations from pathlib import Path diff --git a/src/virtualship/cli/_fetch.py b/src/virtualship/cli/_fetch.py deleted file mode 100644 index 9722fe36..00000000 --- a/src/virtualship/cli/_fetch.py +++ /dev/null @@ -1,240 +0,0 @@ -from __future__ import annotations - -import hashlib -import shutil -from datetime import datetime -from pathlib import Path -from typing import TYPE_CHECKING - -import copernicusmarine -from copernicusmarine.core_functions.credentials_utils import InvalidUsernameOrPassword -from pydantic import BaseModel - -from virtualship.errors import IncompleteDownloadError -from virtualship.utils import ( - _dump_yaml, - _generic_load_yaml, - _get_expedition, - get_input_dataset_class, -) - -if TYPE_CHECKING: - from virtualship.models import SpaceTimeRegion - -import click - -import virtualship.cli._creds as creds -from virtualship.utils import EXPEDITION - -DOWNLOAD_METADATA = "download_metadata.yaml" - - -def _fetch(path: str | Path, username: str | None, password: str | None) -> None: - """ - Download input data for an expedition. - - Entrypoint for the tool to download data based on space-time region provided in the - schedule file. Data is downloaded from Copernicus Marine, credentials for which can be - obtained via registration: https://data.marine.copernicus.eu/register . Credentials can - be provided on prompt, via command line arguments, or via a YAML config file. Run - `virtualship fetch` on an expedition for more info. - """ - # ################################# TEMPORARY TIMER: START ################################# - import time - - start_time = time.time() - print("[TIMER] Expedition started...") - # ################################# TEMPORARY TIMER: START ################################# - - if sum([username is None, password is None]) == 1: - raise ValueError("Both username and password must be provided when using CLI.") - - path = Path(path) - - data_dir = path / "data" - data_dir.mkdir(exist_ok=True) - - expedition = _get_expedition(path) - - expedition.schedule.verify( - expedition.ship_config.ship_speed_knots, - bathy_data_dir=None, - check_space_time_region=True, - ignore_missing_bathymetry=True, - ) - - space_time_region_hash = get_space_time_region_hash( - expedition.schedule.space_time_region - ) - - existing_download = get_existing_download(data_dir, space_time_region_hash) - if existing_download is not None: - click.echo( - f"Data download for space-time region already completed ('{existing_download}')." - ) - return - - creds_path = path / creds.CREDENTIALS_FILE - credentials = {} - credentials["username"], credentials["password"] = creds.get_credentials_flow( - username, password, creds_path - ) - - # Extract instruments and space_time_region details from expedition - instruments_in_expedition = expedition.get_instruments() - space_time_region = expedition.schedule.space_time_region - - # Create download folder and set download metadata - download_folder = data_dir / hash_to_filename(space_time_region_hash) - download_folder.mkdir() - DownloadMetadata(download_complete=False).to_yaml( - download_folder / DOWNLOAD_METADATA - ) - shutil.copyfile(path / EXPEDITION, download_folder / EXPEDITION) - - click.echo(f"\n\n{(' Fetching data for: Bathymetry ').center(80, '=')}\n\n") - - # bathymetry (for all expeditions) - copernicusmarine.subset( - dataset_id="cmems_mod_glo_phy_my_0.083deg_static", - variables=["deptho"], - minimum_longitude=space_time_region.spatial_range.minimum_longitude, - maximum_longitude=space_time_region.spatial_range.maximum_longitude, - minimum_latitude=space_time_region.spatial_range.minimum_latitude, - maximum_latitude=space_time_region.spatial_range.maximum_latitude, - start_datetime=space_time_region.time_range.start_time, - end_datetime=space_time_region.time_range.start_time, - minimum_depth=abs(space_time_region.spatial_range.minimum_depth), - maximum_depth=abs(space_time_region.spatial_range.maximum_depth), - output_filename="bathymetry.nc", - output_directory=download_folder, - username=credentials["username"], - password=credentials["password"], - overwrite=True, - coordinates_selection_method="outside", - ) - - # download, only instruments present in the expedition - for itype in instruments_in_expedition: - input_dataset_class = get_input_dataset_class(itype) - if input_dataset_class is None: - raise RuntimeError(f"No input dataset class found for type {itype}.") - click.echo( - f"\n\n{(' Fetching data for: ' + itype.value + ' ').center(80, '=')}\n\n" - ) - try: - input_dataset = input_dataset_class( - data_dir=download_folder, - credentials=credentials, - space_time_region=space_time_region, - ) - input_dataset.download_data() - except InvalidUsernameOrPassword as e: - shutil.rmtree(download_folder) - raise e - click.echo(f"{itype.value} data download completed.") - - complete_download(download_folder) - - ################################# TEMPORARY TIMER: END ################################# - end_time = time.time() - elapsed = end_time - start_time - print(f"[TIMER] Expedition completed in {elapsed:.2f} seconds.") - ################################# TEMPORARY TIMER: END ################################# - - -def _hash(s: str, *, length: int) -> str: - """Create a hash of a string.""" - assert length % 2 == 0, "Length must be even." - half_length = length // 2 - - return hashlib.shake_128(s.encode("utf-8")).hexdigest(half_length) - - -def create_hash(s: str) -> str: - """Create an 8 digit hash of a string.""" - return _hash(s, length=8) - - -def hash_model(model: BaseModel, salt: int = 0) -> str: - """ - Hash a Pydantic model. - - :param region: The region to hash. - :param salt: Salt to add to the hash. - :returns: The hash. - """ - return create_hash(model.model_dump_json() + str(salt)) - - -def get_space_time_region_hash(space_time_region: SpaceTimeRegion) -> str: - # Increment salt in the event of breaking data fetching changes with prior versions - # of virtualship where you want to force new hashes (i.e., new data downloads) - salt = 0 - return hash_model(space_time_region, salt=salt) - - -def filename_to_hash(filename: str) -> str: - """Extract hash from filename of the format YYYYMMDD_HHMMSS_{hash}.""" - parts = filename.split("_") - if len(parts) != 3: - raise ValueError( - f"Filename '{filename}' must have 3 parts delimited with underscores." - ) - return parts[-1] - - -def hash_to_filename(hash: str) -> str: - """Return a filename of the format YYYYMMDD_HHMMSS_{hash}.""" - if "_" in hash: - raise ValueError("Hash cannot contain underscores.") - return f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{hash}" - - -class DownloadMetadata(BaseModel): - """Metadata for a data download.""" - - download_complete: bool - download_date: datetime | None = None - - def to_yaml(self, file_path: str | Path) -> None: - with open(file_path, "w") as file: - _dump_yaml(self, file) - - @classmethod - def from_yaml(cls, file_path: str | Path) -> DownloadMetadata: - return _generic_load_yaml(file_path, cls) - - -def get_existing_download(data_dir: Path, space_time_region_hash: str) -> Path | None: - """Check if a download has already been completed. If so, return the path for existing download.""" - for download_path in data_dir.rglob("*"): - try: - hash = filename_to_hash(download_path.name) - except ValueError: - continue - if hash == space_time_region_hash: - assert_complete_download(download_path) - return download_path - return None - - -def assert_complete_download(download_path: Path) -> None: - download_metadata = download_path / DOWNLOAD_METADATA - try: - with open(download_metadata) as file: - assert DownloadMetadata.from_yaml(file).download_complete - except (FileNotFoundError, AssertionError) as e: - raise IncompleteDownloadError( - f"Download at {download_path} was found, but looks to be incomplete " - f"(likely due to interupting it mid-download). Please delete this folder and retry." - ) from e - return - - -def complete_download(download_path: Path) -> None: - """Mark a download as complete.""" - download_metadata = download_path / DOWNLOAD_METADATA - metadata = DownloadMetadata(download_complete=True, download_date=datetime.now()) - metadata.to_yaml(download_metadata) - return diff --git a/src/virtualship/cli/_run.py b/src/virtualship/cli/_run.py index 4eb50c66..df8a24fd 100644 --- a/src/virtualship/cli/_run.py +++ b/src/virtualship/cli/_run.py @@ -7,7 +7,6 @@ import pyproj -from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash from virtualship.expedition.simulate_schedule import ( MeasurementsToSimulate, ScheduleProblem, @@ -34,12 +33,11 @@ logging.getLogger("copernicusmarine").setLevel("ERROR") -def _run(expedition_dir: str | Path, from_copernicusmarine: bool) -> None: +def _run(expedition_dir: str | Path) -> None: """ Perform an expedition, providing terminal feedback and file output. :param expedition_dir: The base directory for the expedition. - :param from_copernicusmarine: Whether to use direct data ingestion from Copernicus Marine. Should be determined by CLI flag. """ # ################################# TEMPORARY TIMER: START ################################# import time @@ -70,16 +68,7 @@ def _run(expedition_dir: str | Path, from_copernicusmarine: bool) -> None: print("\n---- WAYPOINT VERIFICATION ----") - # verify schedule is valid - if from_copernicusmarine: - bathy_data_dir = None - else: - bathy_data_dir = get_existing_download( - expedition_dir, - get_space_time_region_hash(expedition.schedule.space_time_region), - ) - - expedition.schedule.verify(expedition.ship_config.ship_speed_knots, bathy_data_dir) + expedition.schedule.verify(expedition.ship_config.ship_speed_knots) # simulate the schedule schedule_results = simulate_schedule( @@ -133,7 +122,6 @@ def _run(expedition_dir: str | Path, from_copernicusmarine: bool) -> None: instrument = instrument_class( expedition=expedition, directory=expedition_dir, - from_copernicusmarine=from_copernicusmarine, ) # execute simulation diff --git a/src/virtualship/cli/commands.py b/src/virtualship/cli/commands.py index e2a4aade..b17f3cbc 100644 --- a/src/virtualship/cli/commands.py +++ b/src/virtualship/cli/commands.py @@ -3,7 +3,6 @@ import click from virtualship import utils -from virtualship.cli._fetch import _fetch from virtualship.cli._plan import _plan from virtualship.cli._run import _run from virtualship.utils import ( @@ -76,48 +75,12 @@ def plan(path): _plan(Path(path)) -@click.command() -@click.argument( - "path", - type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True), -) -@click.option( - "--username", - type=str, - default=None, - help="Copernicus Marine username.", -) -@click.option( - "--password", - type=str, - default=None, - help="Copernicus Marine password.", -) -def fetch(path: str | Path, username: str | None, password: str | None) -> None: - """ - Download input data for an expedition. - - Entrypoint for the tool to download data based on space-time region provided in the - schedule file. Data is downloaded from Copernicus Marine, credentials for which can be - obtained via registration: https://data.marine.copernicus.eu/register . Credentials can - be provided on prompt, via command line arguments, or via a YAML config file. Run - `virtualship fetch` on a expedition for more info. - """ - _fetch(path, username, password) - - # TODO: also add option to 'stream' via link to dir elsewhere, e.g. simlink or path to data stored elsewhere that isn't expedition dir! @click.command() @click.argument( "path", type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True), ) -@click.option( - "--from-copernicusmarine", - is_flag=True, - default=False, - help="Ingest fieldsets directly via copernicusmarine toolbox.", -) -def run(path, from_copernicusmarine: bool): +def run(path): """Run the expedition.""" - _run(Path(path), from_copernicusmarine) + _run(Path(path)) diff --git a/src/virtualship/cli/main.py b/src/virtualship/cli/main.py index 6ee12eff..a02a5ffb 100644 --- a/src/virtualship/cli/main.py +++ b/src/virtualship/cli/main.py @@ -11,7 +11,6 @@ def cli(): cli.add_command(commands.init) cli.add_command(commands.plan) -cli.add_command(commands.fetch) cli.add_command(commands.run) if __name__ == "__main__": diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index 2702cbfd..d01b675b 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -4,10 +4,9 @@ import numpy as np from parcels import ParticleSet, ScipyParticle, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import ( - register_input_dataset, register_instrument, ) @@ -46,46 +45,6 @@ def _sample_velocity(particle, fieldset, time): ) -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.ADCP) -class ADCPInputDataset(InputDataset): - """Input dataset for ADCP instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 0.0, - "days": 0.0, - } # ADCP data requires no buffers - - DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - ADCP.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - space_time_region.spatial_range.minimum_depth, - space_time_region.spatial_range.maximum_depth, - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "UVdata": { - "physical": True, - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -95,7 +54,7 @@ def get_datasets_dict(self) -> dict: class ADCPInstrument(Instrument): """ADCP instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize ADCPInstrument.""" filenames = { "U": f"{ADCP.name}_uv.nc", @@ -111,7 +70,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 30def124..ce539d6b 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -12,10 +12,10 @@ StatusCode, Variable, ) -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import register_instrument # ===================================================== # SECTION: Dataclass @@ -128,56 +128,6 @@ def _check_error(particle, fieldset, time): particle.delete() -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.ARGO_FLOAT) -class ArgoFloatInputDataset(InputDataset): - """Input dataset for ArgoFloat instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 3.0, - "days": 21.0, - } - - DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - ArgoFloat.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - self.DOWNLOAD_LIMITS["min_depth"], - space_time_region.spatial_range.maximum_depth, - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "UVdata": { - "physical": True, - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, - "Sdata": { - "physical": True, - "variables": ["so"], - "output_filename": f"{self.name}_s.nc", - }, - "Tdata": { - "physical": True, - "variables": ["thetao"], - "output_filename": f"{self.name}_t.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -187,7 +137,7 @@ def get_datasets_dict(self) -> dict: class ArgoFloatInstrument(Instrument): """ArgoFloat instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize ArgoFloatInstrument.""" filenames = { "U": f"{ArgoFloat.name}_uv.nc", @@ -205,7 +155,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index b040c3db..311d83f6 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -8,8 +8,7 @@ import xarray as xr from yaspin import yaspin -from parcels import Field, FieldSet -from virtualship.cli._fetch import get_existing_download, get_space_time_region_hash +from parcels import FieldSet from virtualship.utils import ( COPERNICUSMARINE_BGC_VARIABLES, COPERNICUSMARINE_PHYS_VARIABLES, @@ -19,86 +18,7 @@ ) if TYPE_CHECKING: - from virtualship.models import Expedition, SpaceTimeRegion - - -class InputDataset(abc.ABC): - """Base class for instrument input datasets.""" - - # TODO: data download is performed per instrument (in `fetch`), which is a bit inefficient when some instruments can share dataa. - # TODO: However, future changes, with Parcels-v4 and copernicusmarine direct ingestion, will hopefully remove the need for fetch. - - def __init__( - self, - name: str, - latlon_buffer: float, - datetime_buffer: float, - min_depth: float, - max_depth: float, - data_dir: str, - credentials: dict, - space_time_region: "SpaceTimeRegion", - ): - """Initialise input dataset.""" - self.name = name - self.latlon_buffer = latlon_buffer - self.datetime_buffer = datetime_buffer - self.min_depth = min_depth - self.max_depth = max_depth - self.data_dir = data_dir - self.credentials = credentials - self.space_time_region = space_time_region - - @abc.abstractmethod - def get_datasets_dict(self) -> dict: - """Get parameters for instrument's variable(s) specific data download.""" - - def download_data(self) -> None: - """Download data for the instrument using copernicusmarine, with correct product ID selection.""" - parameter_args = dict( - minimum_longitude=self.space_time_region.spatial_range.minimum_longitude - - self.latlon_buffer, - maximum_longitude=self.space_time_region.spatial_range.maximum_longitude - + self.latlon_buffer, - minimum_latitude=self.space_time_region.spatial_range.minimum_latitude - - self.latlon_buffer, - maximum_latitude=self.space_time_region.spatial_range.maximum_latitude - + self.latlon_buffer, - start_datetime=self.space_time_region.time_range.start_time, - end_datetime=self.space_time_region.time_range.end_time - + timedelta(days=self.datetime_buffer), - minimum_depth=abs(self.min_depth), - maximum_depth=abs(self.max_depth), - output_directory=self.data_dir, - username=self.credentials["username"], - password=self.credentials["password"], - overwrite=True, - coordinates_selection_method="outside", - ) - - datasets_args = self.get_datasets_dict() - - for dataset in datasets_args.values(): - physical = dataset.get("physical") - if physical: - variable = None - else: - variable = dataset.get("variables")[0] # BGC variables, special case - - dataset_id = _select_product_id( - physical=physical, - schedule_start=self.space_time_region.time_range.start_time, - schedule_end=self.space_time_region.time_range.end_time, - username=self.credentials["username"], - password=self.credentials["password"], - variable=variable, - ) - download_args = { - **parameter_args, - **{k: v for k, v in dataset.items() if k != "physical"}, - "dataset_id": dataset_id, - } - copernicusmarine.subset(**download_args) + from virtualship.models import Expedition class Instrument(abc.ABC): @@ -107,7 +27,6 @@ class Instrument(abc.ABC): #! TODO List: # TODO: update documentation/quickstart # TODO: update tests - # TODO: if use direct ingestion as primary data sourcing, can substantially cut code base (including _fetch.py, InputDataset objects). Consider this for Parcels v4 transition. #! TODO: how is this handling credentials?! Seems to work already, are these set up from my previous instances of using copernicusmarine? Therefore users will only have to do it once too? def __init__( @@ -121,7 +40,6 @@ def __init__( allow_time_extrapolation: bool, verbose_progress: bool, bathymetry_file: str = "bathymetry.nc", - from_copernicusmarine: bool = False, ): """Initialise instrument.""" self.name = name @@ -139,61 +57,40 @@ def __init__( self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation self.verbose_progress = verbose_progress - self.from_copernicusmarine = from_copernicusmarine def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" - if self.from_copernicusmarine: - try: - datasets = [] - for var in self.variables.values(): - physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False - - # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! - if self.name == "Drifter": - ds = self._get_copernicus_ds_DRIFTER(physical=physical, var=var) - else: - ds = self._get_copernicus_ds(physical=physical, var=var) - datasets.append(ds) - - # make sure time dims are matched if BGC variables are present (different monthly/daily resolutions can impact fieldset_endtime in simulate) - if any( - key in COPERNICUSMARINE_BGC_VARIABLES - for key in ds.keys() - for ds in datasets - ): - datasets = self._align_temporal(datasets) - - ds_concat = xr.merge(datasets) # TODO: deal with WARNINGS? - - fieldset = FieldSet.from_xarray_dataset( - ds_concat, self.variables, self.dimensions, mesh="spherical" - ) - - except Exception as e: - raise FileNotFoundError( - f"Failed to load input data directly from Copernicus Marine for instrument '{self.name}'. " - f"Please check your credentials, network connection, and variable names. Original error: {e}" - ) from e + try: + datasets = [] + for var in self.variables.values(): + physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False + + # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! + if self.name == "Drifter": + ds = self._get_copernicus_ds_DRIFTER(physical=physical, var=var) + else: + ds = self._get_copernicus_ds(physical=physical, var=var) + datasets.append(ds) + + # make sure time dims are matched if BGC variables are present (different monthly/daily resolutions can impact fieldset_endtime in simulate) + if any( + key in COPERNICUSMARINE_BGC_VARIABLES + for key in ds.keys() + for ds in datasets + ): + datasets = self._align_temporal(datasets) + + ds_concat = xr.merge(datasets) # TODO: deal with WARNINGS? + + fieldset = FieldSet.from_xarray_dataset( + ds_concat, self.variables, self.dimensions, mesh="spherical" + ) - else: # from fetched data on disk - try: - data_dir = self._get_data_dir(self.directory) - joined_filepaths = { - key: data_dir.joinpath(filename) - for key, filename in self.filenames.items() - } - fieldset = FieldSet.from_netcdf( - joined_filepaths, - self.variables, - self.dimensions, - allow_time_extrapolation=self.allow_time_extrapolation, - ) - except FileNotFoundError as e: - raise FileNotFoundError( - f"Input data for instrument {self.name} not found locally. Have you run the `virtualship fetch` command?" - "Alternatively, you can use the `--from-copernicusmarine` option to ingest data directly from Copernicus Marine." - ) from e + except Exception as e: + raise FileNotFoundError( + f"Failed to load input data directly from Copernicus Marine for instrument '{self.name}'. " + f"Please check your credentials, network connection, and variable names. Original error: {e}" + ) from e # interpolation methods for var in (v for v in self.variables if v not in ("U", "V")): @@ -203,19 +100,11 @@ def load_input_data(self) -> FieldSet: g.negate_depth() # bathymetry data - if self.add_bathymetry: - if self.from_copernicusmarine: - bathymetry_field = _get_bathy_data( - self.expedition.schedule.space_time_region - ).bathymetry - else: - bathymetry_field = Field.from_netcdf( - data_dir.joinpath(self.bathymetry_file), - variable=("bathymetry", "deptho"), - dimensions={"lon": "longitude", "lat": "latitude"}, - ) - bathymetry_field.data = -bathymetry_field.data - fieldset.add_field(bathymetry_field) + bathymetry_field = _get_bathy_data( + self.expedition.schedule.space_time_region + ).bathymetry + bathymetry_field.data = -bathymetry_field.data + fieldset.add_field(bathymetry_field) return fieldset @@ -240,18 +129,6 @@ def execute(self, measurements: list, out_path: str | Path) -> None: # self.simulate(measurements, out_path) - def _get_data_dir(self, expedition_dir: Path) -> Path: - space_time_region_hash = get_space_time_region_hash( - self.expedition.schedule.space_time_region - ) - data_dir = get_existing_download(expedition_dir, space_time_region_hash) - - assert data_dir is not None, ( - "Input data hasn't been found. Have you run the `virtualship fetch` command?" - ) - - return data_dir - def _get_copernicus_ds( self, physical: bool, diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 7434da2d..d205795b 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -5,12 +5,12 @@ import numpy as np from parcels import JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType if TYPE_CHECKING: from virtualship.models.spacetime import Spacetime -from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_instrument # ===================================================== # SECTION: Dataclass @@ -70,49 +70,6 @@ def _ctd_cast(particle, fieldset, time): particle.delete() -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.CTD) -class CTDInputDataset(InputDataset): - """Input dataset for CTD instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 0.0, - "days": 0.0, - } # CTD data requires no buffers - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - CTD.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - space_time_region.spatial_range.minimum_depth, - space_time_region.spatial_range.maximum_depth, - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "Sdata": { - "physical": True, - "variables": ["so"], - "output_filename": f"{self.name}_s.nc", - }, - "Tdata": { - "physical": True, - "variables": ["thetao"], - "output_filename": f"{self.name}_t.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -122,7 +79,7 @@ def get_datasets_dict(self) -> dict: class CTDInstrument(Instrument): """CTD instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize CTDInstrument.""" filenames = { "S": f"{CTD.name}_s.nc", @@ -139,7 +96,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 505e4bcc..182d274c 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -5,10 +5,10 @@ import numpy as np from parcels import JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_instrument # ===================================================== # SECTION: Dataclass @@ -92,74 +92,6 @@ def _ctd_bgc_cast(particle, fieldset, time): particle.delete() -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.CTD_BGC) -class CTD_BGCInputDataset(InputDataset): - """Input dataset object for CTD_BGC instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 0.0, - "days": 0.0, - } # CTD_BGC data requires no buffers - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - CTD_BGC.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - space_time_region.spatial_range.minimum_depth, - space_time_region.spatial_range.maximum_depth, - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Variable specific args for instrument.""" - return { - "o2data": { - "physical": False, - "variables": ["o2"], - "output_filename": f"{self.name}_o2.nc", - }, - "chlorodata": { - "physical": False, - "variables": ["chl"], - "output_filename": f"{self.name}_chl.nc", - }, - "nitratedata": { - "physical": False, - "variables": ["no3"], - "output_filename": f"{self.name}_no3.nc", - }, - "phosphatedata": { - "physical": False, - "variables": ["po4"], - "output_filename": f"{self.name}_po4.nc", - }, - "phdata": { - "physical": False, - "variables": ["ph"], - "output_filename": f"{self.name}_ph.nc", - }, - "phytoplanktondata": { - "physical": False, - "variables": ["phyc"], - "output_filename": f"{self.name}_phyc.nc", - }, - "primaryproductiondata": { - "physical": False, - "variables": ["nppv"], - "output_filename": f"{self.name}_nppv.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -169,7 +101,7 @@ def get_datasets_dict(self) -> dict: class CTD_BGCInstrument(Instrument): """CTD_BGC instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize CTD_BGCInstrument.""" filenames = { "o2": f"{CTD_BGC.name}_o2.nc", @@ -198,7 +130,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 182100c2..06f6b71f 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -5,10 +5,10 @@ import numpy as np from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import register_instrument # ===================================================== # SECTION: Dataclass @@ -54,51 +54,6 @@ def _check_lifetime(particle, fieldset, time): particle.delete() -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.DRIFTER) -class DrifterInputDataset(InputDataset): - """Input dataset for Drifter instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 3.0, - "days": 21.0, - } - - DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1, "max_depth": 1} - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - Drifter.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - self.DOWNLOAD_LIMITS["min_depth"], - self.DOWNLOAD_LIMITS["max_depth"], - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "UVdata": { - "physical": True, - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, - "Tdata": { - "physical": True, - "variables": ["thetao"], - "output_filename": f"{self.name}_t.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -108,7 +63,7 @@ def get_datasets_dict(self) -> dict: class DrifterInstrument(Instrument): """Drifter instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize DrifterInstrument.""" filenames = { "U": f"{Drifter.name}_uv.nc", @@ -125,7 +80,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 5f793d3d..f6099869 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -4,9 +4,9 @@ import numpy as np from parcels import ParticleSet, ScipyParticle, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType -from virtualship.utils import add_dummy_UV, register_input_dataset, register_instrument +from virtualship.utils import add_dummy_UV, register_instrument # ===================================================== # SECTION: Dataclass @@ -46,51 +46,6 @@ def _sample_temperature(particle, fieldset, time): particle.T = fieldset.T[time, particle.depth, particle.lat, particle.lon] -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.UNDERWATER_ST) -class Underwater_STInputDataset(InputDataset): - """Input dataset for Underwater_ST instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 0.0, - "days": 0.0, - } # Underwater_ST data requires no buffers - - DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - Underwater_ST.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - -2.0, # is always at 2m depth - -2.0, # is always at 2m depth - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "Sdata": { - "physical": True, - "variables": ["so"], - "output_filename": f"{self.name}_s.nc", - }, - "Tdata": { - "physical": True, - "variables": ["thetao"], - "output_filename": f"{self.name}_t.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -100,7 +55,7 @@ def get_datasets_dict(self) -> dict: class Underwater_STInstrument(Instrument): """Underwater_ST instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize Underwater_STInstrument.""" filenames = { "S": f"{Underwater_ST.name}_s.nc", @@ -117,7 +72,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index ab11ed67..fd88240d 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -5,10 +5,10 @@ import numpy as np from parcels import JITParticle, ParticleSet, Variable -from virtualship.instruments.base import InputDataset, Instrument +from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime -from virtualship.utils import register_input_dataset, register_instrument +from virtualship.utils import register_instrument # ===================================================== # SECTION: Dataclass @@ -68,56 +68,6 @@ def _xbt_cast(particle, fieldset, time): particle_ddepth = particle.max_depth - particle.depth -# ===================================================== -# SECTION: InputDataset Class -# ===================================================== - - -@register_input_dataset(InstrumentType.XBT) -class XBTInputDataset(InputDataset): - """Input dataset for XBT instrument.""" - - DOWNLOAD_BUFFERS: ClassVar[dict] = { - "latlon_degrees": 3.0, - "days": 21.0, - } - - DOWNLOAD_LIMITS: ClassVar[dict] = {"min_depth": 1} - - def __init__(self, data_dir, credentials, space_time_region): - """Initialise with instrument's name.""" - super().__init__( - XBT.name, - self.DOWNLOAD_BUFFERS["latlon_degrees"], - self.DOWNLOAD_BUFFERS["days"], - self.DOWNLOAD_LIMITS["min_depth"], - space_time_region.spatial_range.maximum_depth, - data_dir, - credentials, - space_time_region, - ) - - def get_datasets_dict(self) -> dict: - """Get variable specific args for instrument.""" - return { - "UVdata": { - "physical": True, - "variables": ["uo", "vo"], - "output_filename": f"{self.name}_uv.nc", - }, - "Sdata": { - "physical": True, - "variables": ["so"], - "output_filename": f"{self.name}_s.nc", - }, - "Tdata": { - "physical": True, - "variables": ["thetao"], - "output_filename": f"{self.name}_t.nc", - }, - } - - # ===================================================== # SECTION: Instrument Class # ===================================================== @@ -127,7 +77,7 @@ def get_datasets_dict(self) -> dict: class XBTInstrument(Instrument): """XBT instrument class.""" - def __init__(self, expedition, directory, from_copernicusmarine): + def __init__(self, expedition, directory): """Initialize XBTInstrument.""" filenames = { "U": f"{XBT.name}_uv.nc", @@ -145,7 +95,6 @@ def __init__(self, expedition, directory, from_copernicusmarine): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, - from_copernicusmarine=from_copernicusmarine, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index 14d9537d..aa310a97 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -2,14 +2,12 @@ import itertools from datetime import datetime, timedelta -from pathlib import Path from typing import TYPE_CHECKING import pydantic import pyproj import yaml -from parcels import Field from virtualship.errors import InstrumentsConfigError, ScheduleError from virtualship.instruments.types import InstrumentType from virtualship.utils import _get_bathy_data, _validate_numeric_mins_to_timedelta @@ -89,7 +87,6 @@ class Schedule(pydantic.BaseModel): def verify( self, ship_speed: float, - bathy_data_dir: str | Path | None, ignore_missing_bathymetry: bool = False, *, check_space_time_region: bool = False, @@ -108,7 +105,7 @@ def verify( if check_space_time_region and self.space_time_region is None: raise ScheduleError( - "space_time_region not found in schedule, please define it to fetch the data." + "space_time_region not found in schedule, please define it to proceed." ) if len(self.waypoints) == 0: @@ -133,28 +130,14 @@ def verify( # TODO: write test that checks that will flag when waypoint is on land!! [add to existing suite of fail .verify() tests in test_expedition.py] land_waypoints = [] if not ignore_missing_bathymetry: - if bathy_data_dir is None: - try: - bathymetry_field = _get_bathy_data( - self.space_time_region - ).bathymetry # via copernicusmarine - except Exception as e: - raise ScheduleError( - f"Problem loading bathymetry data (used to verify waypoints are in water) directly via copernicusmarine. \n\n original message: {e}" - ) from e - - else: - bathymetry_path = bathy_data_dir.joinpath("bathymetry.nc") - try: - bathymetry_field = Field.from_netcdf( - bathymetry_path, - variable=("bathymetry", "deptho"), - dimensions={"lon": "longitude", "lat": "latitude"}, - ) - except Exception as e: - raise ScheduleError( - f"Problem loading local bathymetry data (used to verify waypoints are in water). Have you run `virtualship fetch` command?. \n\n original message: {e}" - ) from e + try: + bathymetry_field = _get_bathy_data( + self.space_time_region + ).bathymetry # via copernicusmarine + except Exception as e: + raise ScheduleError( + f"Problem loading bathymetry data (used to verify waypoints are in water) directly via copernicusmarine. \n\n original message: {e}" + ) from e for wp_i, wp in enumerate(self.waypoints): try: diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index dc46ded7..4ea17c4a 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -249,19 +249,10 @@ def _get_expedition(expedition_dir: Path) -> Expedition: ) -# InstrumentType -> InputDataset and Instrument registry and registration utilities. -INPUT_DATASET_MAP = {} +# InstrumentType -> Instrument registry and registration utilities. INSTRUMENT_CLASS_MAP = {} -def register_input_dataset(instrument_type): - def decorator(cls): - INPUT_DATASET_MAP[instrument_type] = cls - return cls - - return decorator - - def register_instrument(instrument_type): def decorator(cls): INSTRUMENT_CLASS_MAP[instrument_type] = cls @@ -270,10 +261,6 @@ def decorator(cls): return decorator -def get_input_dataset_class(instrument_type): - return INPUT_DATASET_MAP.get(instrument_type) - - def get_instrument_class(instrument_type): return INSTRUMENT_CLASS_MAP.get(instrument_type) From 05686f2513229286bff5cbf3bee3789353dcad2a Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:24:21 +0100 Subject: [PATCH 54/56] update docstrings/--help info --- src/virtualship/cli/commands.py | 4 ++-- src/virtualship/expedition/__init__.py | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/virtualship/cli/commands.py b/src/virtualship/cli/commands.py index b17f3cbc..2b0ce8e9 100644 --- a/src/virtualship/cli/commands.py +++ b/src/virtualship/cli/commands.py @@ -68,7 +68,7 @@ def init(path, from_mfp): ) def plan(path): """ - Launch UI to help build schedule and ship config files. + Launch UI to help build expedition configuration (YAML) file. Should you encounter any issues with using this tool, please report an issue describing the problem to the VirtualShip issue tracker at: https://github.com/OceanParcels/virtualship/issues" """ @@ -82,5 +82,5 @@ def plan(path): type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True), ) def run(path): - """Run the expedition.""" + """Execute the expedition simulations.""" _run(Path(path)) diff --git a/src/virtualship/expedition/__init__.py b/src/virtualship/expedition/__init__.py index dfa61028..7f072bbf 100644 --- a/src/virtualship/expedition/__init__.py +++ b/src/virtualship/expedition/__init__.py @@ -1,7 +1 @@ -"""Everything for simulating an expedition.""" - -from .do_expedition import do_expedition - -__all__ = [ - "do_expedition", -] +"""Simulating an expedition.""" From 10e68b383a640038804d8effe3c22129d1f85c27 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:40:19 +0100 Subject: [PATCH 55/56] add buffers to drifters and argos, plus depth limits for drifters --- src/virtualship/instruments/adcp.py | 3 + src/virtualship/instruments/argo_float.py | 7 ++ src/virtualship/instruments/base.py | 85 ++++++------------- src/virtualship/instruments/ctd.py | 2 + src/virtualship/instruments/ctd_bgc.py | 2 + src/virtualship/instruments/drifter.py | 13 ++- .../instruments/ship_underwater_st.py | 2 + src/virtualship/instruments/xbt.py | 2 + src/virtualship/models/expedition.py | 5 +- src/virtualship/utils.py | 14 +-- 10 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index d01b675b..dd675b7e 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -61,6 +61,7 @@ def __init__(self, expedition, directory): "V": f"{ADCP.name}_uv.nc", } variables = {"U": "uo", "V": "vo"} + super().__init__( ADCP.name, expedition, @@ -70,6 +71,8 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, + buffer_spec=None, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index ce539d6b..12ee8945 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -146,6 +146,11 @@ def __init__(self, expedition, directory): "T": f"{ArgoFloat.name}_t.nc", } variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"} + buffer_spec = { + "latlon": 6.0, # [degrees] + "time": 21.0, # [days] + } + super().__init__( ArgoFloat.name, expedition, @@ -155,6 +160,8 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, + buffer_spec=buffer_spec, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index 311d83f6..c290f277 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -25,9 +25,7 @@ class Instrument(abc.ABC): """Base class for instruments and their simulation.""" #! TODO List: - # TODO: update documentation/quickstart - # TODO: update tests - #! TODO: how is this handling credentials?! Seems to work already, are these set up from my previous instances of using copernicusmarine? Therefore users will only have to do it once too? + # TODO: how is this handling credentials?! Seems to work already, are these set up from my previous instances of using copernicusmarine? Therefore users will only have to do it once too? def __init__( self, @@ -39,7 +37,8 @@ def __init__( add_bathymetry: bool, allow_time_extrapolation: bool, verbose_progress: bool, - bathymetry_file: str = "bathymetry.nc", + buffer_spec: dict | None = None, + limit_spec: dict | None = None, ): """Initialise instrument.""" self.name = name @@ -53,10 +52,11 @@ def __init__( "time": "time", "depth": "depth", } # same dimensions for all instruments - self.bathymetry_file = bathymetry_file self.add_bathymetry = add_bathymetry self.allow_time_extrapolation = allow_time_extrapolation self.verbose_progress = verbose_progress + self.buffer_spec = buffer_spec + self.limit_spec = limit_spec def load_input_data(self) -> FieldSet: """Load and return the input data as a FieldSet for the instrument.""" @@ -64,20 +64,11 @@ def load_input_data(self) -> FieldSet: datasets = [] for var in self.variables.values(): physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False - - # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! - if self.name == "Drifter": - ds = self._get_copernicus_ds_DRIFTER(physical=physical, var=var) - else: - ds = self._get_copernicus_ds(physical=physical, var=var) - datasets.append(ds) + datasets.append(self._get_copernicus_ds(physical=physical, var=var)) # make sure time dims are matched if BGC variables are present (different monthly/daily resolutions can impact fieldset_endtime in simulate) - if any( - key in COPERNICUSMARINE_BGC_VARIABLES - for key in ds.keys() - for ds in datasets - ): + all_keys = set().union(*(ds.keys() for ds in datasets)) + if all_keys & set(COPERNICUSMARINE_BGC_VARIABLES): datasets = self._align_temporal(datasets) ds_concat = xr.merge(datasets) # TODO: deal with WARNINGS? @@ -100,11 +91,15 @@ def load_input_data(self) -> FieldSet: g.negate_depth() # bathymetry data - bathymetry_field = _get_bathy_data( - self.expedition.schedule.space_time_region - ).bathymetry - bathymetry_field.data = -bathymetry_field.data - fieldset.add_field(bathymetry_field) + if self.add_bathymetry: + bathymetry_field = _get_bathy_data( + self.expedition.schedule.space_time_region, + latlon_buffer=self.buffer_spec.get("latlon") + if self.buffer_spec + else None, + ).bathymetry + bathymetry_field.data = -bathymetry_field.data + fieldset.add_field(bathymetry_field) return fieldset @@ -127,8 +122,6 @@ def execute(self, measurements: list, out_path: str | Path) -> None: self.simulate(measurements, out_path) print("\n") - # self.simulate(measurements, out_path) - def _get_copernicus_ds( self, physical: bool, @@ -142,50 +135,28 @@ def _get_copernicus_ds( variable=var if not physical else None, ) - return copernicusmarine.open_dataset( - dataset_id=product_id, - dataset_part="default", - minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude, - maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude, - minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude, - maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude, - variables=[var], - start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, - end_datetime=self.expedition.schedule.space_time_region.time_range.end_time, - coordinates_selection_method="outside", - ) + latlon_buffer = self.buffer_spec.get("latlon") if self.buffer_spec else 0.0 + time_buffer = self.buffer_spec.get("time") if self.buffer_spec else 0.0 - # TODO: TEMPORARY BODGE FOR DRIFTER INSTRUMENT - REMOVE WHEN ABLE TO! - def _get_copernicus_ds_DRIFTER( - self, - physical: bool, - var: str, - ) -> xr.Dataset: - """Get Copernicus Marine dataset for direct ingestion.""" - product_id = _select_product_id( - physical=physical, - schedule_start=self.expedition.schedule.space_time_region.time_range.start_time, - schedule_end=self.expedition.schedule.space_time_region.time_range.end_time, - variable=var if not physical else None, - ) + depth_min = self.limit_spec.get("depth_min") if self.limit_spec else None + depth_max = self.limit_spec.get("depth_max") if self.limit_spec else None return copernicusmarine.open_dataset( dataset_id=product_id, - dataset_part="default", minimum_longitude=self.expedition.schedule.space_time_region.spatial_range.minimum_longitude - - 3.0, + - latlon_buffer, maximum_longitude=self.expedition.schedule.space_time_region.spatial_range.maximum_longitude - + 3.0, + + latlon_buffer, minimum_latitude=self.expedition.schedule.space_time_region.spatial_range.minimum_latitude - - 3.0, + - latlon_buffer, maximum_latitude=self.expedition.schedule.space_time_region.spatial_range.maximum_latitude - + 3.0, - maximum_depth=1.0, - minimum_depth=1.0, + + latlon_buffer, variables=[var], start_datetime=self.expedition.schedule.space_time_region.time_range.start_time, end_datetime=self.expedition.schedule.space_time_region.time_range.end_time - + timedelta(days=21.0), + + timedelta(days=time_buffer), + minimum_depth=depth_min, + maximum_depth=depth_max, coordinates_selection_method="outside", ) diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index d205795b..9bd92353 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -96,6 +96,8 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + buffer_spec=None, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 182d274c..4c286f3c 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -130,6 +130,8 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + buffer_spec=None, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index 06f6b71f..fee4e326 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -71,6 +71,15 @@ def __init__(self, expedition, directory): "T": f"{Drifter.name}_t.nc", } variables = {"U": "uo", "V": "vo", "T": "thetao"} + buffer_spec = { + "latlon": 6.0, # [degrees] + "time": 21.0, # [days] + } + limit_spec = { + "depth_min": 1.0, # [meters] + "depth_max": 1.0, # [meters] + } + super().__init__( Drifter.name, expedition, @@ -80,6 +89,8 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=False, verbose_progress=True, + buffer_spec=buffer_spec, + limit_spec=limit_spec, ) def simulate(self, measurements, out_path) -> None: @@ -121,7 +132,7 @@ def simulate(self, measurements, out_path) -> None: chunks=[len(drifter_particleset), 100], ) - # get earliest between fieldset end time and provide end time + # get earliest between fieldset end time and prescribed end time fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1]) if ENDTIME is None: actual_endtime = fieldset_endtime diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index f6099869..32dcdd4f 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -72,6 +72,8 @@ def __init__(self, expedition, directory): add_bathymetry=False, allow_time_extrapolation=True, verbose_progress=False, + buffer_spec=None, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index fd88240d..2d6a7079 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -95,6 +95,8 @@ def __init__(self, expedition, directory): add_bathymetry=True, allow_time_extrapolation=True, verbose_progress=False, + buffer_spec=None, + limit_spec=None, ) def simulate(self, measurements, out_path) -> None: diff --git a/src/virtualship/models/expedition.py b/src/virtualship/models/expedition.py index aa310a97..0cb13955 100644 --- a/src/virtualship/models/expedition.py +++ b/src/virtualship/models/expedition.py @@ -132,7 +132,7 @@ def verify( if not ignore_missing_bathymetry: try: bathymetry_field = _get_bathy_data( - self.space_time_region + self.space_time_region, latlon_buffer=None ).bathymetry # via copernicusmarine except Exception as e: raise ScheduleError( @@ -406,9 +406,6 @@ def verify(self, expedition: Expedition) -> None: hasattr(self, config_attr) and inst_type not in instruments_in_expedition ): - print( - f"{inst_type.value} configuration provided but not in schedule. Removing config." - ) setattr(self, config_attr, None) # Check all scheduled instruments are configured for inst_type in instruments_in_expedition: diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 4ea17c4a..7e863034 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -412,14 +412,18 @@ def _start_end_in_product_timerange( ) -def _get_bathy_data(space_time_region) -> FieldSet: +def _get_bathy_data(space_time_region, latlon_buffer: float | None = None) -> FieldSet: """Bathymetry data 'streamed' directly from Copernicus Marine.""" ds_bathymetry = copernicusmarine.open_dataset( dataset_id="cmems_mod_glo_phy_my_0.083deg_static", - minimum_longitude=space_time_region.spatial_range.minimum_longitude, - maximum_longitude=space_time_region.spatial_range.maximum_longitude, - minimum_latitude=space_time_region.spatial_range.minimum_latitude, - maximum_latitude=space_time_region.spatial_range.maximum_latitude, + minimum_longitude=space_time_region.spatial_range.minimum_longitude + - (latlon_buffer if latlon_buffer is not None else 0), + maximum_longitude=space_time_region.spatial_range.maximum_longitude + + (latlon_buffer if latlon_buffer is not None else 0), + minimum_latitude=space_time_region.spatial_range.minimum_latitude + - (latlon_buffer if latlon_buffer is not None else 0), + maximum_latitude=space_time_region.spatial_range.maximum_latitude + + (latlon_buffer if latlon_buffer is not None else 0), variables=["deptho"], start_datetime=space_time_region.time_range.start_time, end_datetime=space_time_region.time_range.end_time, From 66e5b88e436bbf73aa12c4c8c986ef8cfb27155d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:47:20 +0000 Subject: [PATCH 56/56] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/virtualship/instruments/adcp.py | 2 +- src/virtualship/instruments/argo_float.py | 2 +- src/virtualship/instruments/base.py | 2 +- src/virtualship/instruments/ctd.py | 2 +- src/virtualship/instruments/ctd_bgc.py | 2 +- src/virtualship/instruments/drifter.py | 2 +- src/virtualship/instruments/ship_underwater_st.py | 2 +- src/virtualship/instruments/xbt.py | 2 +- src/virtualship/utils.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/virtualship/instruments/adcp.py b/src/virtualship/instruments/adcp.py index dd675b7e..e524c5a2 100644 --- a/src/virtualship/instruments/adcp.py +++ b/src/virtualship/instruments/adcp.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np - from parcels import ParticleSet, ScipyParticle, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import ( diff --git a/src/virtualship/instruments/argo_float.py b/src/virtualship/instruments/argo_float.py index 12ee8945..f064e81a 100644 --- a/src/virtualship/instruments/argo_float.py +++ b/src/virtualship/instruments/argo_float.py @@ -4,7 +4,6 @@ from typing import ClassVar import numpy as np - from parcels import ( AdvectionRK4, JITParticle, @@ -12,6 +11,7 @@ StatusCode, Variable, ) + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index c290f277..f5a28b2f 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -6,9 +6,9 @@ import copernicusmarine import numpy as np import xarray as xr +from parcels import FieldSet from yaspin import yaspin -from parcels import FieldSet from virtualship.utils import ( COPERNICUSMARINE_BGC_VARIABLES, COPERNICUSMARINE_PHYS_VARIABLES, diff --git a/src/virtualship/instruments/ctd.py b/src/virtualship/instruments/ctd.py index 9bd92353..5ec02e41 100644 --- a/src/virtualship/instruments/ctd.py +++ b/src/virtualship/instruments/ctd.py @@ -3,8 +3,8 @@ from typing import TYPE_CHECKING, ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType diff --git a/src/virtualship/instruments/ctd_bgc.py b/src/virtualship/instruments/ctd_bgc.py index 4c286f3c..33e14aca 100644 --- a/src/virtualship/instruments/ctd_bgc.py +++ b/src/virtualship/instruments/ctd_bgc.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/drifter.py b/src/virtualship/instruments/drifter.py index fee4e326..035a81b0 100644 --- a/src/virtualship/instruments/drifter.py +++ b/src/virtualship/instruments/drifter.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import AdvectionRK4, JITParticle, ParticleSet, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/instruments/ship_underwater_st.py b/src/virtualship/instruments/ship_underwater_st.py index 32dcdd4f..1067c03e 100644 --- a/src/virtualship/instruments/ship_underwater_st.py +++ b/src/virtualship/instruments/ship_underwater_st.py @@ -2,8 +2,8 @@ from typing import ClassVar import numpy as np - from parcels import ParticleSet, ScipyParticle, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.utils import add_dummy_UV, register_instrument diff --git a/src/virtualship/instruments/xbt.py b/src/virtualship/instruments/xbt.py index 2d6a7079..9ec76527 100644 --- a/src/virtualship/instruments/xbt.py +++ b/src/virtualship/instruments/xbt.py @@ -3,8 +3,8 @@ from typing import ClassVar import numpy as np - from parcels import JITParticle, ParticleSet, Variable + from virtualship.instruments.base import Instrument from virtualship.instruments.types import InstrumentType from virtualship.models.spacetime import Spacetime diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index 7e863034..22e8f498 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -10,8 +10,8 @@ import copernicusmarine import numpy as np - from parcels import FieldSet + from virtualship.errors import CopernicusCatalogueError if TYPE_CHECKING:

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 &x{`0BG|NY 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 M$i%Z2@r?-mKe+IE761SM diff --git a/docs/user-guide/assignments/Code_of_conduct.ipynb b/docs/user-guide/assignments/Code_of_conduct.ipynb new file mode 100644 index 00000000..470add25 --- /dev/null +++ b/docs/user-guide/assignments/Code_of_conduct.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code of conduct\n", + "_As used during the Dynamical Oceanography 2024/25 course at Utrecht University_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Please read this code of conduct, fill out the [parts between brackets] after consultation with your group, remove these italic sections, and then all individually upload a copy._ \n", + "\n", + "_Note that the Code of Conduct applies to the actual collaboration within your group in the Virtual Ship Assignment; so is not about how you would work on the ship._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This code of conduct has been decided by [NAMES] for their group project during the Virtual Ship Classroom and is enacted from [DD-MM-YYYY]. The procedure is based on [this exercise by Aurelia Moser](http://aureliamoser.com/aaas-guides/conduct/index.html)\n", + "\n", + "Everyone taking part in the course and group discussions (mentors, helpers, coordinators, and learners) is required to conform to the following Code of Conduct. Coordinators will oversee adherence to this code throughout the course.\n", + "\n", + "Characteristics we value: [FILL IN]\n", + "\n", + "Behaviors we encourage: [FILL IN]\n", + "\n", + "Behaviors we discourage: [FILL IN]\n", + "\n", + "The way we redistribute the grade: [FILL IN]\n", + "\n", + "Choose for example “Everyone in the team gets the same grade”, “Everyone can propose a bonus point to one other person, which is then subtracted from other team member’s grades”, “We decide as a group who gets up to x bonus/penalty points”, or any other way to redistribute the grade, as long as the average of the group is not affected. \n", + "\n", + "We make each others feel safe and supported by: [FILL IN]\n", + "\n", + "How to report an issue, should someone violate the code?\n", + "1.\tContact the course instructor by private message, or in person. All communication will be treated as confidential.\n", + "2.\tIf for any reason you don’t want to do 1, you can contact the university Academic Integrity Counsellor." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Community Participation Guidelines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Apart from the code of conduct, all participants in this course must follow these general guidelines.\n", + "Participation \n", + "\n", + "When participating in Dynamical Oceanography, respect the Utrecht University guideline for academic and scientific integrity and code of conduct. These guidelines cover our behaviour as participants, mentors, experts, staff, volunteers, and anyone else involved in making this course possible.\n", + "\n", + "How to treat each other\n", + "\n", + "To create a collaborative and inviting learning environment, we also emphasise certain values in how we treat each other:\n", + "*\tBe respectful and value each other’s ideas, styles and viewpoints.\n", + "*\tBe direct but professional; we cannot withhold hard truths.\n", + "*\tBe inclusive and help new perspectives be heard.\n", + "*\tAppreciate and accommodate our many cultural practices, attitudes and beliefs.\n", + "*\tBe open to learning from others.\n", + "*\tLead by example and match your actions with your words.\n", + "\n", + "The following will not be tolerated during the activities related to this course:\n", + "*\tviolence and threats of violence.\n", + "*\tpersonal attacks; derogatory language.\n", + "*\tunwelcome sexual attention or physical contact.\n", + "*\tdisruptive behavior.\n", + "*\tinfluencing unacceptable behavior.\n", + "\n", + "### Inclusion and Diversity\n", + "\n", + "We welcome contributions from everyone as long as they interact constructively with our community, including, but not limited to people of varied age, culture, ethnicity, gender, gender-identity, language, race, sexual orientation, geographical location and religious views.\n", + "\n", + "Raising Issues\n", + "\n", + "If you believe you‘re experiencing practices which don‘t meet the above policies, or if you feel you are being harassed in any way, please immediately contact the course coordinator, or the university Academic Integrity Counsellor.\n", + "\n", + "The course coordinator reserves the right to refuse admission to anyone violating these policies, and/or take further action including reporting to the responsible figure for academic integrity at the department.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Thumbnail image](_images/freepik_code_of_conduct.jpg)" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/user-guide/assignments/Sail_the_ship.ipynb b/docs/user-guide/assignments/Sail_the_ship.ipynb index 3f62d06a..8bfcd463 100644 --- a/docs/user-guide/assignments/Sail_the_ship.ipynb +++ b/docs/user-guide/assignments/Sail_the_ship.ipynb @@ -177,6 +177,13 @@ "![Our ocean provides](https://seewhatgrows.org/wp-content/uploads/2019/06/our-ocean.jpg)\n", "![10 ways to help our ocean](https://oceanservice.noaa.gov/ocean/help-ocean.jpg)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Thumbnail image](_images/freepik_research_vessel.jpg)" + ] } ], "metadata": { diff --git a/docs/user-guide/assignments/index.md b/docs/user-guide/assignments/index.md index 2b37970f..0e0db109 100644 --- a/docs/user-guide/assignments/index.md +++ b/docs/user-guide/assignments/index.md @@ -5,9 +5,9 @@ maxdepth: 1 caption: Assignments --- +Virtualship_research_proposal.ipynb Research_proposal_intro.ipynb Research_Proposal_only.ipynb -Virtualship_research_proposal.ipynb sciencecommunication_assignment.ipynb ``` From 01dd168b5def800748fab64c5e54541037e1fd47 Mon Sep 17 00:00:00 2001 From: Emma Daniels <135691556+ammedd@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:03:20 +0100 Subject: [PATCH 43/56] add teacher info to documentation (#213) * add teacher info to documentation * incoporate review suggestions * fix default thumbnail * change ILOs to ipynb --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/conf.py | 3 + docs/user-guide/_images/ILOs.jpg | Bin 0 -> 173260 bytes docs/user-guide/index.md | 1 + docs/user-guide/teacher-content/ILOs.ipynb | 69 +++++++++++++++++++++ docs/user-guide/teacher-content/index.md | 26 ++++++++ 5 files changed, 99 insertions(+) create mode 100644 docs/user-guide/_images/ILOs.jpg create mode 100644 docs/user-guide/teacher-content/ILOs.ipynb create mode 100644 docs/user-guide/teacher-content/index.md diff --git a/docs/conf.py b/docs/conf.py index 91cf56f8..27353cbb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -78,4 +78,7 @@ "user-guide/assignments/Sail_the_ship": "user-guide/_images/freepik_research_vessel.jpg", "user-guide/assignments/Code_of_conduct": "user-guide/_images/freepik_code_of_conduct.jpg", } + +sphinx_gallery_conf = {"default_thumb_file": "_static/virtual_ship_logo.png"} + nbsphinx_execute = "never" diff --git a/docs/user-guide/_images/ILOs.jpg b/docs/user-guide/_images/ILOs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..81c0952331e1af6b00623e0a8664a1ebc271bdba GIT binary patch literal 173260 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%0Dvb2NtQk6-bgfgl%_VJ$>?$L{sn}9U2}r}Y8R#mJ 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