Skip to content

Commit

Permalink
More generic environment mapping methods, from vectors to magnitude/d…
Browse files Browse the repository at this point in the history
…irection 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
  • Loading branch information
knutfrode committed Aug 23, 2023
1 parent 4a31631 commit cd1fd2a
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 37 deletions.
8 changes: 5 additions & 3 deletions opendrift/readers/basereader/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
]
68 changes: 50 additions & 18 deletions opendrift/readers/basereader/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand All @@ -473,27 +482,49 @@ 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'],
'method': land_binary_mask_from_ocean_depth,
'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]],
Expand All @@ -513,17 +544,18 @@ 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:
em = self.environment_mappings[m]
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'])
Expand Down
22 changes: 8 additions & 14 deletions opendrift/scripts/readerinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions tests/readers/test_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit cd1fd2a

Please sign in to comment.