11import abc
2+ from collections import OrderedDict
23from datetime import timedelta
34from pathlib import Path
45from typing import TYPE_CHECKING
89from yaspin import yaspin
910
1011from parcels import FieldSet
12+ from virtualship .errors import CopernicusCatalogueError
1113from virtualship .utils import (
1214 COPERNICUSMARINE_PHYS_VARIABLES ,
1315 _get_bathy_data ,
@@ -43,7 +45,8 @@ def __init__(
4345 self .expedition = expedition
4446 self .directory = directory
4547 self .filenames = filenames
46- self .variables = variables
48+
49+ self .variables = OrderedDict (variables )
4750 self .dimensions = {
4851 "lon" : "longitude" ,
4952 "lat" : "latitude" ,
@@ -61,8 +64,8 @@ def load_input_data(self) -> FieldSet:
6164 try :
6265 fieldset = self ._generate_fieldset ()
6366 except Exception as e :
64- raise FileNotFoundError (
65- f"Failed to load input data directly from Copernicus Marine for instrument '{ self .name } '.Original error: { e } "
67+ raise CopernicusCatalogueError (
68+ f"Failed to load input data directly from Copernicus Marine for instrument '{ self .name } '. Original error: { e } "
6669 ) from e
6770
6871 # interpolation methods
@@ -92,22 +95,18 @@ def simulate(self, data_dir: Path, measurements: list, out_path: str | Path):
9295
9396 def execute (self , measurements : list , out_path : str | Path ) -> None :
9497 """Run instrument simulation."""
95- TMP = False
96- if not TMP :
97- if not self .verbose_progress :
98- with yaspin (
99- text = f"Simulating { self .name } measurements... " ,
100- side = "right" ,
101- spinner = ship_spinner ,
102- ) as spinner :
103- self .simulate (measurements , out_path )
104- spinner .ok ("✅\n " )
105- else :
106- print (f"Simulating { self .name } measurements... " )
98+ if not self .verbose_progress :
99+ with yaspin (
100+ text = f"Simulating { self .name } measurements... " ,
101+ side = "right" ,
102+ spinner = ship_spinner ,
103+ ) as spinner :
107104 self .simulate (measurements , out_path )
108- print ( " \n " )
105+ spinner . ok ( "✅ \n " )
109106 else :
107+ print (f"Simulating { self .name } measurements... " )
110108 self .simulate (measurements , out_path )
109+ print ("\n " )
111110
112111 def _get_copernicus_ds (
113112 self ,
@@ -122,11 +121,10 @@ def _get_copernicus_ds(
122121 variable = var if not physical else None ,
123122 )
124123
125- latlon_buffer = self .buffer_spec .get ("latlon" ) if self .buffer_spec else 0.0
126- time_buffer = self .buffer_spec .get ("time" ) if self .buffer_spec else 0.0
127-
128- depth_min = self .limit_spec .get ("depth_min" ) if self .limit_spec else None
129- depth_max = self .limit_spec .get ("depth_max" ) if self .limit_spec else None
124+ latlon_buffer = self ._get_spec_value ("buffer" , "latlon" , 0.0 )
125+ time_buffer = self ._get_spec_value ("buffer" , "time" , 0.0 )
126+ depth_min = self ._get_spec_value ("limit" , "depth_min" , None )
127+ depth_max = self ._get_spec_value ("limit" , "depth_max" , None )
130128
131129 return copernicusmarine .open_dataset (
132130 dataset_id = product_id ,
@@ -151,21 +149,27 @@ def _generate_fieldset(self) -> FieldSet:
151149 """
152150 Fieldset per variable then combine.
153151
154- Avoids issues when creating one FieldSet of ds's sourced from different Copernicus Marine product IDs, which is often the case for BGC variables.
155-
152+ Avoids issues when creating directly one FieldSet of ds's sourced from different Copernicus Marine product IDs, which is often the case for BGC variables.
156153 """
157154 fieldsets_list = []
158- for key , var in self .variables .items ():
155+ keys = list (self .variables .keys ())
156+ for key in keys :
157+ var = self .variables [key ]
159158 physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False
160159 ds = self ._get_copernicus_ds (physical = physical , var = var )
161- fieldset = FieldSet .from_xarray_dataset (
160+ fs = FieldSet .from_xarray_dataset (
162161 ds , {key : var }, self .dimensions , mesh = "spherical"
163162 )
164- fieldsets_list .append (fieldset )
163+ fieldsets_list .append (fs )
164+
165165 base_fieldset = fieldsets_list [0 ]
166166 if len (fieldsets_list ) > 1 :
167- for fs , key in zip (
168- fieldsets_list [1 :], list (self .variables .keys ())[1 :], strict = True
169- ):
167+ for fs , key in zip (fieldsets_list [1 :], keys [1 :], strict = True ):
170168 base_fieldset .add_field (getattr (fs , key ))
169+
171170 return base_fieldset
171+
172+ def _get_spec_value (self , spec_type : str , key : str , default = None ):
173+ """Helper to extract a value from buffer_spec or limit_spec."""
174+ spec = self .buffer_spec if spec_type == "buffer" else self .limit_spec
175+ return spec .get (key ) if spec and spec .get (key ) is not None else default
0 commit comments