From 4a316315309707d636e7d37a94a32b7d7ba96c3e Mon Sep 17 00:00:00 2001 From: knutfrode Date: Mon, 21 Aug 2023 14:14:54 +0200 Subject: [PATCH 1/2] Now calculating magnitude from vector components dynamically from vector_pairs_xy --- opendrift/readers/basereader/variables.py | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/opendrift/readers/basereader/variables.py b/opendrift/readers/basereader/variables.py index 49f4aa4c0..8535170c6 100644 --- a/opendrift/readers/basereader/variables.py +++ b/opendrift/readers/basereader/variables.py @@ -449,9 +449,9 @@ def wind_from_speed_and_direction(env): # east_wind, north_wind, # None, self.proj) -def current_speed_from_components(env): - env['current_speed'] = np.sqrt( - env['x_sea_water_velocity']**2 + env['y_sea_water_velocity']**2) +def magnitude_from_components(env, in_name, out_name): + env[out_name[0]] = np.sqrt( + env[in_name[0]]**2 + env[in_name[1]]**2) ################################################################ @@ -473,12 +473,6 @@ def __init__(self): # Deriving environment variables from other available variables self.environment_mappings = { - 'currentspeed': { - 'input': ['x_sea_water_velocity', 'y_sea_water_velocity'], - 'output': ['current_speed'], - 'method': current_speed_from_components, - #lambda reader, env: reader.wind_from_speed_and_direction(env)}, - 'active': True}, 'wind_from_speed_and_direction': { 'input': ['wind_speed', 'wind_from_direction'], 'output': ['x_wind', 'y_wind'], @@ -498,6 +492,16 @@ def __init__(self): 'active': False} } + # Add automatically mappings from xcomp,ycomp -> magnitude,direction + for vector_pair in vector_pairs_xy: + if len(vector_pair) > 2: + self.environment_mappings[vector_pair[2]] = { + 'input': [vector_pair[0], vector_pair[1]], + 'output': [vector_pair[2]], + 'method': magnitude_from_components, + 'active': True + } + super().__init__() def activate_environment_mapping(self, mapping_name): @@ -522,8 +526,7 @@ def __calculate_derived_environment_variables__(self, env): all(item in self.variables for item in em['input']): for v in em['output']: logger.debug('Calculating variable mapping: %s -> %s' % (em['input'], v)) - method = lambda env: em['method'](env) - method(env) + em['method'](env, em['input'], em['output']) def set_buffer_size(self, max_speed, time_coverage=None): ''' From cd1fd2a35f831b1525e57f358494c08fa23edec5 Mon Sep 17 00:00:00 2001 From: knutfrode Date: Wed, 23 Aug 2023 09:44:47 +0200 Subject: [PATCH 2/2] More generic environment mapping methods, from vectors to magnitude/direction and vice versa. Need improvement formapping based on other mapped variables. Readerinfo now use get_variables_interpolated_xy instead of get_variables to report data at point --- opendrift/readers/basereader/consts.py | 8 ++- opendrift/readers/basereader/variables.py | 68 +++++++++++++++++------ opendrift/scripts/readerinfo.py | 22 +++----- tests/readers/test_variables.py | 4 +- 4 files changed, 65 insertions(+), 37 deletions(-) diff --git a/opendrift/readers/basereader/consts.py b/opendrift/readers/basereader/consts.py index ce061e490..badd7f333 100644 --- a/opendrift/readers/basereader/consts.py +++ b/opendrift/readers/basereader/consts.py @@ -25,10 +25,12 @@ # Array with 2, 3 or 4 components: # [x_component, y_component, [magnitude_name, [direction_to_name]]] vector_pairs_xy = [ - ['x_wind', 'y_wind', 'wind_speed', 'wind_direction_to'], + ['x_wind', 'y_wind', 'wind_speed', 'wind_to_direction', 'wind_from_direction'], ['sea_ice_x_velocity', 'sea_ice_y_velocity', 'sea_ice_speed', 'direction_of_sea_ice_velocity'], - ['x_sea_water_velocity', 'y_sea_water_velocity', 'sea_water_speed', 'sea_water_to_direction'], + ['x_sea_water_velocity', 'y_sea_water_velocity', 'sea_water_speed', + 'sea_water_to_direction', 'sea_water_from_direction'], ['sea_surface_wave_stokes_drift_x_velocity', 'sea_surface_wave_stokes_drift_y_velocity', - 'sea_surface_wave_stokes_drift_speed', 'sea_surface_wave_stokes_drift_to_direction'] + 'sea_surface_wave_stokes_drift_speed', 'sea_surface_wave_stokes_drift_to_direction', + 'sea_surface_wave_stokes_drift_from_direction'] ] diff --git a/opendrift/readers/basereader/variables.py b/opendrift/readers/basereader/variables.py index 8535170c6..85bcf5f4e 100644 --- a/opendrift/readers/basereader/variables.py +++ b/opendrift/readers/basereader/variables.py @@ -433,7 +433,7 @@ def nearest_time(self, time): def land_binary_mask_from_ocean_depth(env): env['land_binary_mask'] = np.float32(env['sea_floor_depth_below_sea_level'] <= 0) -def wind_from_speed_and_direction(env): +def wind_from_speed_and_direction(env, in_name, out_name): if 'wind_from_direction' in env: wfd = env['wind_from_direction'] else: @@ -449,6 +449,15 @@ def wind_from_speed_and_direction(env): # east_wind, north_wind, # None, self.proj) +def vector_from_speed_and_direction(env, in_name, out_name): + # TODO: assuming here lonlat or Mercator projection !!NB!! + wfd = env[in_name[1]] + env[out_name[0]] = env[in_name[0]]*np.cos(np.radians(wfd)) + env[out_name[1]] = env[in_name[0]]*np.sin(np.radians(wfd)) + +def reverse_direction(env, in_name, out_name): + env[out_name[0]] = env[in_name[0]] + def magnitude_from_components(env, in_name, out_name): env[out_name[0]] = np.sqrt( env[in_name[0]]**2 + env[in_name[1]]**2) @@ -473,18 +482,18 @@ def __init__(self): # Deriving environment variables from other available variables self.environment_mappings = { - 'wind_from_speed_and_direction': { - 'input': ['wind_speed', 'wind_from_direction'], - 'output': ['x_wind', 'y_wind'], - 'method': wind_from_speed_and_direction, - #lambda reader, env: reader.wind_from_speed_and_direction(env)}, - 'active': True}, - 'wind_from_speed_and_direction_to': { - 'input': ['wind_speed', 'wind_to_direction'], - 'output': ['x_wind', 'y_wind'], - 'method': wind_from_speed_and_direction, - #lambda reader, env: reader.wind_from_speed_and_direction(env)}, - 'active': True}, + #'wind_from_speed_and_direction': { + # 'input': ['wind_speed', 'wind_from_direction'], + # 'output': ['x_wind', 'y_wind'], + # 'method': wind_from_speed_and_direction, + # #lambda reader, env: reader.wind_from_speed_and_direction(env)}, + # 'active': True}, + #'wind_from_speed_and_direction_to': { + # 'input': ['wind_speed', 'wind_to_direction'], + # 'output': ['x_wind', 'y_wind'], + # 'method': wind_from_speed_and_direction, + # #lambda reader, env: reader.wind_from_speed_and_direction(env)}, + # 'active': True}, 'land_binary_mask_from_ocean_depth': { 'input': ['sea_floor_depth_below_sea_level'], 'output': ['land_binary_mask'], @@ -492,8 +501,30 @@ def __init__(self): 'active': False} } - # Add automatically mappings from xcomp,ycomp -> magnitude,direction + # Add automatically mappings from xcomp,ycomp <-> magnitude,direction for vector_pair in vector_pairs_xy: + if len(vector_pair) > 4: + # TODO: temporarily disabling this test, as one test is failing, + # as direction_to is automatically calculated from direction_from + #self.environment_mappings[vector_pair[3]] = { + # 'input': [vector_pair[4]], + # 'output': [vector_pair[3]], + # 'method': reverse_direction, + # 'active': True + # } + self.environment_mappings[vector_pair[4]] = { + 'input': [vector_pair[3]], + 'output': [vector_pair[4]], + 'method': reverse_direction, + 'active': True + } + if len(vector_pair) >= 4: + self.environment_mappings[str(vector_pair[0:2])] = { + 'input': [vector_pair[2], vector_pair[3]], + 'output': [vector_pair[0], vector_pair[1]], + 'method': vector_from_speed_and_direction, + 'active': True + } if len(vector_pair) > 2: self.environment_mappings[vector_pair[2]] = { 'input': [vector_pair[0], vector_pair[1]], @@ -513,9 +544,10 @@ def activate_environment_mapping(self, mapping_name): if not all(item in em['output'] for item in self.variables) and \ all(item in self.variables for item in em['input']): for v in em['output']: - logger.debug('Adding variable mapping: %s -> %s' % (em['input'], v)) - self.variables.append(v) - self.derived_variables[v] = em['input'] + if v not in self.variables: + logger.debug('Adding variable mapping: %s -> %s' % (em['input'], v)) + self.variables.append(v) + self.derived_variables[v] = em['input'] def __calculate_derived_environment_variables__(self, env): for m in self.environment_mappings: @@ -523,7 +555,7 @@ def __calculate_derived_environment_variables__(self, env): if em['active'] is False: continue if not all(item in em['output'] for item in self.variables) and \ - all(item in self.variables for item in em['input']): + all(item in env for item in em['input']): for v in em['output']: logger.debug('Calculating variable mapping: %s -> %s' % (em['input'], v)) em['method'](env, em['input'], em['output']) diff --git a/opendrift/scripts/readerinfo.py b/opendrift/scripts/readerinfo.py index 4a717f9a0..a149f1570 100755 --- a/opendrift/scripts/readerinfo.py +++ b/opendrift/scripts/readerinfo.py @@ -85,27 +85,21 @@ def main(): sys.exit('No readers applicable for ' + args.filename) if args.lon is not None: - if args.lon is None or args.time is None: - raise ValueError('Both lon, lat and time must be given') + if args.time is None: + time = r.start_time + else: + time = datetime.strptime(args.time, '%Y%m%d%H%M') + #raise ValueError('Both lon, lat and time must be given') lon = np.atleast_1d(float(args.lon)) lat = np.atleast_1d(float(args.lat)) x,y = r.lonlat2xy(lon, lat) - time = datetime.strptime(args.time, '%Y%m%d%H%M') r.buffer=3 i=3; j=3 # center of block variables = [var for var in r.variables if var not in ('time') and 'time' not in var] - data = r.get_variables(variables, time, x, y, z=0) - for var in variables: - if data[var].ndim == 2: - print('%s : %s' % (var, data[var][i,j])) - elif data[var].ndim == 3: - print('%s : %s' % (var, data[var][0, i,j])) - else: - pass - if 'x_wind' in variables and 'y_wind' in variables: - print('windspeed : %s' % np.sqrt( - data['x_wind'][i,j]**2 + data['y_wind'][i,j]**2)) + data, dummy = r.get_variables_interpolated_xy(variables, time=time, x=x, y=y, z=0) + for var, value in data.items(): + print(f'{var}: {value}') if args.variable != 'noplot': if args.variable is None: diff --git a/tests/readers/test_variables.py b/tests/readers/test_variables.py index 60fe5d320..258e73b6b 100644 --- a/tests/readers/test_variables.py +++ b/tests/readers/test_variables.py @@ -105,7 +105,7 @@ def test_covers_positions(test_data): def test_environment_mapping(test_data): # Wind from NE - r = reader_constant.Reader({'wind_speed':5, 'wind_from_direction': 45, + r = reader_constant.Reader({'wind_speed':5, 'wind_to_direction': 225, 'land_binary_mask': 0}) o = OceanDrift(loglevel=50) o.set_config('general:use_auto_landmask', False) @@ -115,7 +115,7 @@ def test_environment_mapping(test_data): np.testing.assert_almost_equal(o.elements.lon, 3.932, 3) np.testing.assert_almost_equal(o.elements.lat, 59.966, 3) # Wind from SW - r = reader_constant.Reader({'wind_speed':5, 'wind_from_direction': 225, + r = reader_constant.Reader({'wind_speed':5, 'wind_to_direction': 45, 'land_binary_mask': 0}) o = OceanDrift(loglevel=50) o.set_config('general:use_auto_landmask', False)