44from typing import TYPE_CHECKING
55
66import copernicusmarine
7- import numpy as np
87import xarray as xr
98from yaspin import yaspin
109
1110from parcels import FieldSet
1211from virtualship .utils import (
13- COPERNICUSMARINE_BGC_VARIABLES ,
1412 COPERNICUSMARINE_PHYS_VARIABLES ,
1513 _get_bathy_data ,
1614 _select_product_id ,
@@ -61,31 +59,16 @@ def __init__(
6159 def load_input_data (self ) -> FieldSet :
6260 """Load and return the input data as a FieldSet for the instrument."""
6361 try :
64- datasets = []
65- for var in self .variables .values ():
66- physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False
67- datasets .append (self ._get_copernicus_ds (physical = physical , var = var ))
68-
69- # make sure time dims are matched if BGC variables are present (different monthly/daily resolutions can impact fieldset_endtime in simulate)
70- all_keys = set ().union (* (ds .keys () for ds in datasets ))
71- if all_keys & set (COPERNICUSMARINE_BGC_VARIABLES ):
72- datasets = self ._align_temporal (datasets )
73-
74- ds_concat = xr .merge (datasets ) # TODO: deal with WARNINGS?
75-
76- fieldset = FieldSet .from_xarray_dataset (
77- ds_concat , self .variables , self .dimensions , mesh = "spherical"
78- )
79-
62+ fieldset = self ._generate_fieldset ()
8063 except Exception as e :
8164 raise FileNotFoundError (
82- f"Failed to load input data directly from Copernicus Marine for instrument '{ self .name } '. "
83- f"Please check your credentials, network connection, and variable names. Original error: { e } "
65+ f"Failed to load input data directly from Copernicus Marine for instrument '{ self .name } '.Original error: { e } "
8466 ) from e
8567
8668 # interpolation methods
8769 for var in (v for v in self .variables if v not in ("U" , "V" )):
8870 getattr (fieldset , var ).interp_method = "linear_invdist_land_tracer"
71+
8972 # depth negative
9073 for g in fieldset .gridset .grids :
9174 g .negate_depth ()
@@ -109,18 +92,22 @@ def simulate(self, data_dir: Path, measurements: list, out_path: str | Path):
10992
11093 def execute (self , measurements : list , out_path : str | Path ) -> None :
11194 """Run instrument simulation."""
112- if not self .verbose_progress :
113- with yaspin (
114- text = f"Simulating { self .name } measurements... " ,
115- side = "right" ,
116- spinner = ship_spinner ,
117- ) as spinner :
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... " )
118107 self .simulate (measurements , out_path )
119- spinner . ok ( "✅ \n " )
108+ print ( " \n " )
120109 else :
121- print (f"Simulating { self .name } measurements... " )
122110 self .simulate (measurements , out_path )
123- print ("\n " )
124111
125112 def _get_copernicus_ds (
126113 self ,
@@ -160,19 +147,25 @@ def _get_copernicus_ds(
160147 coordinates_selection_method = "outside" ,
161148 )
162149
163- def _align_temporal (self , datasets : list [xr .Dataset ]) -> list [xr .Dataset ]:
164- """Align monthly and daily time dims of multiple datasets (by repeating monthly values daily)."""
165- reference_time = datasets [
166- np .argmax (ds .time for ds in datasets )
167- ].time # daily timeseries
168-
169- datasets_aligned = []
170- for ds in datasets :
171- if not np .array_equal (ds .time , reference_time ):
172- # TODO: NEED TO CHOOSE BEST METHOD HERE
173- # ds = ds.resample(time="1D").ffill().reindex(time=reference_time)
174- # ds = ds.resample(time="1D").ffill()
175- ds = ds .reindex ({"time" : reference_time }, method = "nearest" )
176- datasets_aligned .append (ds )
177-
178- return datasets_aligned
150+ def _generate_fieldset (self ) -> FieldSet :
151+ """
152+ Fieldset per variable then combine.
153+
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+
156+ """
157+ fieldsets_list = []
158+ for key , var in self .variables .items ():
159+ physical = True if var in COPERNICUSMARINE_PHYS_VARIABLES else False
160+ ds = self ._get_copernicus_ds (physical = physical , var = var )
161+ fieldset = FieldSet .from_xarray_dataset (
162+ ds , {key : var }, self .dimensions , mesh = "spherical"
163+ )
164+ fieldsets_list .append (fieldset )
165+ base_fieldset = fieldsets_list [0 ]
166+ if len (fieldsets_list ) > 1 :
167+ for fs , key in zip (
168+ fieldsets_list [1 :], list (self .variables .keys ())[1 :], strict = True
169+ ):
170+ base_fieldset .add_field (getattr (fs , key ))
171+ return base_fieldset
0 commit comments