Skip to content

Commit e4ef17a

Browse files
Merge pull request #8 from KonstantinChri/dev
Merging dev for next release
2 parents 23c0218 + fd845f2 commit e4ef17a

24 files changed

+1276
-385
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ More information in [documentation](https://dnora.readthedocs.io/en/latest/)
1111
Example of downscaling NORA3 using dnora/SWAN offshore Tromsø, Norway:
1212
![dnora](https://user-images.githubusercontent.com/67804784/147151236-b9ef920c-34a2-4da0-9877-6241723eff80.gif)
1313

14+
Example of long-crested wave propagation at Stad, Norway, using dnora/SWASH:
15+
![eta](https://user-images.githubusercontent.com/67804784/160290851-ca743601-2ac7-48b5-be52-da3ec8c31e13.gif)
16+
1417

1518
Dnora contains the code of the function read_sms_mesh from the PyFVCOM package (distributed under the MIT License). The original code can be found at https://github.com/pwcazenave/pyfvcom
1619

dnora/aux.py

+127-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .bnd.bnd_mod import Boundary
1313
from .wnd.wnd_mod import Forcing
1414

15-
def distance_2points(lat1,lon1,lat2,lon2) -> float:
15+
def distance_2points(lat1, lon1, lat2, lon2) -> float:
1616
"""Calculate distance between two points"""
1717

1818
R = 6371.0
@@ -35,7 +35,7 @@ def min_distance(lon, lat, lon_vec , lat_vec) -> Tuple[float, int]:
3535
"""
3636

3737
dx = []
38-
for n in range(len(lat_vec)):
38+
for n, __ in enumerate(lat_vec):
3939
dx.append(distance_2points(lat, lon, lat_vec[n], lon_vec[n]))
4040

4141
return np.array(dx).min(), np.array(dx).argmin()
@@ -45,6 +45,130 @@ def lon_in_km(lat: float) -> float:
4545

4646
return distance_2points(lat, 0, lat, 1)
4747

48+
def domain_size_in_km(lon: Tuple(float, float), lat: Tuple(float, float)) -> Tuple[float, float]:
49+
"""Calculates approximate size of grid in km."""
50+
51+
km_x = distance_2points((lat[0]+lat[1])/2, lon[0], (lat[0]+lat[1])/2,lon[1])
52+
km_y = distance_2points(lat[0], lon[0], lat[1], lon[0])
53+
54+
return km_x, km_y
55+
56+
def force_to_xyz(data: np.ndarray, lon: np.ndarray, lat: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
57+
'''If the data is given in a matrix, convert it to xyz vectors.
58+
59+
Does nothing is data is already in xyz.'''
60+
# If the data is in a matrix
61+
if len(data.shape) > 1:
62+
lon0, lat0 = np.meshgrid(lon, lat)
63+
x = lon0.ravel()
64+
y = lat0.ravel()
65+
z = data.ravel()
66+
else:
67+
x = lon
68+
y = lat
69+
z = data
70+
71+
return z, x, y
72+
73+
def set_spacing_dlon_dlat_fixed_edges(dlon: float, dlat:float, lon: Tuple[float, float], lat: Tuple[float, float]) -> Tuple[float, float, float, float, np.ndarray, np.ndarray]:
74+
"""Given dlon, dlat and lon,lat edges, set other spacing varialbles.
75+
76+
Preserves edges (not dlon,dlat)
77+
"""
78+
nx = int((lon[1]-lon[0])/dlon + 1)
79+
ny = int((lat[1]-lat[0])/dlat + 1)
80+
81+
# Define longitudes and latitudes
82+
lon_array = np.linspace(lon[0], lon[1], nx)
83+
lat_array = np.linspace(lat[0], lat[1], ny)
84+
85+
if nx > 1:
86+
dlon = (lon[1]-lon[0])/(nx-1)
87+
else:
88+
dlon = 0.
89+
90+
if ny > 1:
91+
dlat = (lat[1]-lat[0])/(ny-1)
92+
else:
93+
dlat = 0.
94+
95+
lon = (min(lon_array), max(lon_array))
96+
lat = (min(lat_array), max(lat_array))
97+
km_x, km_y = domain_size_in_km(lon, lat)
98+
99+
# dx, dy in metres
100+
dx = km_x*1000/nx
101+
dy = km_y*1000/ny
102+
103+
return dlon, dlat, dx, dy, lon_array, lat_array
104+
105+
def set_spacing_dlon_dlat_floating_edges(dlon: float, dlat:float, lon: Tuple[float, float], lat: Tuple[float, float]) -> Tuple[float, float, float, float, np.ndarray, np.ndarray]:
106+
"""Given dlon, dlat and lon,lat edges, set other spacing varialbles
107+
108+
Preserves dlon, dlat (not edges)
109+
"""
110+
lon_array = np.arange(lon[0],lon[1]+dlon/2,dlon)
111+
lat_array = np.arange(lat[0],lat[1]+dlon/2,dlat)
112+
113+
lon = (min(lon_array), max(lon_array))
114+
lat = (min(lat_array), max(lat_array))
115+
km_x, km_y = domain_size_in_km(lon, lat)
116+
117+
# Number of points
118+
nx = len(lon_array)
119+
ny = len(lat_array)
120+
121+
# dx, dy in metres
122+
dx = km_x*1000/nx
123+
dy = km_y*1000/ny
124+
125+
return dlon, dlat, dx, dy, lon_array, lat_array
126+
127+
def set_spacing_dx_dy(dx: float, dy:float, lon: Tuple[float, float], lat: Tuple[float, float]) -> Tuple[float, float, float, float, np.ndarray, np.ndarray]:
128+
km_x, km_y = domain_size_in_km(lon, lat)
129+
# Number of points
130+
nx = int(np.round(km_x*1000/dx)+1)
131+
ny = int(np.round(km_y*1000/dy)+1)
132+
133+
# dx, dy in metres
134+
dx = km_x*1000/nx
135+
dy = km_y*1000/ny
136+
137+
if nx > 1:
138+
dlon = (lon[1]-lon[0])/(nx-1)
139+
else:
140+
dlon = 0.
141+
if ny > 1:
142+
dlat = (lat[1]-lat[0])/(ny-1)
143+
else:
144+
dlat = 0.
145+
146+
# Define longitudes and latitudes
147+
lon_array = np.linspace(lon[0], lon[1], nx)
148+
lat_array = np.linspace(lat[0], lat[1], ny)
149+
150+
return dlon, dlat, dx, dy, lon_array, lat_array
151+
152+
def set_spacing_nx_ny(nx: float, ny:float, lon: Tuple[float, float], lat: Tuple[float, float]) -> Tuple[float, float, float, float, np.ndarray, np.ndarray]:
153+
# Define longitudes and latitudes
154+
lon_array = np.linspace(lon[0], lon[1], nx)
155+
lat_array = np.linspace(lat[0], lat[1], ny)
156+
if nx > 1:
157+
dlon = (lon[1]-lon[0])/(nx-1)
158+
else:
159+
dlon = 0.
160+
161+
if ny > 1:
162+
dlat = (lat[-1]-lat[0])/(ny-1)
163+
else:
164+
dlat = 0.
165+
166+
km_x, km_y = domain_size_in_km(lon, lat)
167+
# dx, dy in metres
168+
dx = km_x*1000/nx
169+
dy = km_y*1000/ny
170+
171+
return dlon, dlat, dx, dy, lon_array, lat_array
48172

49173
def day_list(start_time, end_time):
50174
"""Determins a Pandas data range of all the days in the time span of the InputModel objext"""
@@ -196,7 +320,7 @@ def create_time_stamps(start_time: str, end_time: str, stride: int, hours_per_fi
196320

197321
# FIND FILE STAMPS
198322
start_stamp = pd.Timestamp(start_time) - pd.DateOffset(hours=lead_time)
199-
if last_file is not '':
323+
if last_file != '':
200324
end_stamp = pd.Timestamp(last_file)
201325

202326
# E.g. we want to start a forecast at 06:00 but the last (and only) file is 00:00

dnora/bnd/bnd_mod.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -165,37 +165,41 @@ def freq(self):
165165
if hasattr(self, 'data'):
166166
return copy(self.data.freq.values)
167167
else:
168-
return None
168+
return np.array([])
169169

170170
def dirs(self):
171171
if hasattr(self, 'data'):
172172
return copy(self.data.dirs.values)
173173
else:
174-
return None
174+
return np.array([])
175175

176176
def lon(self):
177177
if hasattr(self, 'data'):
178178
return copy(self.data.lon.values)
179179
else:
180-
return None
180+
return np.array([])
181181

182182
def lat(self):
183183
if hasattr(self, 'data'):
184184
return copy(self.data.lat.values)
185185
else:
186-
return None
186+
return np.array([])
187187

188188
def x(self):
189189
if hasattr(self, 'data'):
190190
return copy(self.data.x.values)
191191
else:
192-
return None
192+
return np.array([])
193193

194194
def days(self):
195195
"""Determins a Pandas data range of all the days in the time span."""
196196
days = day_list(start_time = self.start_time, end_time = self.end_time)
197197
return days
198198

199+
def size(self):
200+
return (len(self.time()), len(self.x()))
201+
202+
199203
def name(self) -> str:
200204
"""Return the name of the grid (set at initialization)."""
201205
return copy(self._name)

dnora/bnd/read_metno.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ def __call__(self, start_time, end_time, inds) -> Tuple:
6969
url = self.get_url(file_times[n])
7070
msg.from_file(url)
7171
msg.plain(f"Reading boundary spectra: {start_times[n]}-{end_times[n]}")
72-
73-
bnd_list.append(xr.open_dataset(url).sel(time = slice(start_times[n], end_times[n]), x = (inds+1)))
72+
with xr.open_dataset(url) as f:
73+
this_ds = f.sel(time = slice(start_times[n], end_times[n]), x = (inds+1))[['SPEC', 'longitude', 'latitude', 'time', 'freq', 'direction']].copy()
74+
bnd_list.append(this_ds)
7475

7576
msg.info("Merging dataset together (this might take a while)...")
7677
bnd=xr.concat(bnd_list, dim="time").squeeze('y')
@@ -130,7 +131,10 @@ def __call__(self, start_time, end_time, inds) -> Tuple:
130131
url = self.get_url(file_times[n])
131132
msg.from_file(url)
132133
msg.plain(f"Reading boundary spectra: {start_times[n]}-{end_times[n]}")
133-
bnd_list.append(xr.open_dataset(url).sel(time = slice(start_times[n], end_times[n]), x = (inds+1)))
134+
with xr.open_dataset(url) as f:
135+
this_ds = f.sel(time = slice(start_times[n], end_times[n]), x = (inds+1))[['SPEC', 'longitude', 'latitude', 'time', 'freq', 'direction']].copy()
136+
bnd_list.append(this_ds)
137+
#bnd_list.append(xr.open_dataset(url).sel(time = slice(start_times[n], end_times[n]), x = (inds+1)))
134138

135139
bnd=xr.concat(bnd_list, dim="time").squeeze('y')
136140

dnora/defaults.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
dflt_grd = {'fs': { 'SWAN': '#Grid_SWAN',
55
'WW3': '#Grid',
66
'SWASH': '#Grid_SWASH',
7+
'HOS_ocean': '#Grid_HOS_ocean',
78
'General': 'topo_#Grid'},
89
'fldr': {'SWAN': f"{os.getcwd()}/output",
910
'WW3': f"{os.getcwd()}/output",
1011
'SWASH': f"{os.getcwd()}/output",
12+
'HOS_ocean': f"{os.getcwd()}/output",
1113
'General': ''},
1214
'info': { 'SWAN': '#Grid_info.txt',
1315
'WW3': '#Grid_info.txt',
1416
'SWASH': '#Grid_info.txt',
17+
'HOS_ocean': '#Grid_info.txt',
1518
'General': '#Grid_info.txt'},
1619
'ext': { 'SWAN': 'bot',
1720
'WW3': 'txt',
1821
'SWASH': 'bot',
22+
'HOS_ocean': 'dat',
1923
'General': 'txt'}
2024
}
2125

@@ -59,14 +63,24 @@
5963

6064
"""Default filestrings and datestrings for inp-module"""
6165
dflt_inp = {'fs': { 'SWAN': 'input_#T0_#Grid',
62-
'SWASH': 'input_#T0_#T1_#Grid'},
66+
'SWASH': 'input_#T0_#T1_#Grid',
67+
'HOS_ocean': 'input_HOS',
68+
'WW3': 'ww3_',
69+
},
6370
'ds': { 'SWAN': '%Y%m%d',
64-
'SWASH': '%H%M%S'},
71+
'SWASH': '%H%M%S',
72+
'HOS_ocean': '',
73+
'WW3': '',
74+
},
6575
'fldr': {'SWAN': 'MySWANFolder',
66-
'SWASH': 'MySWASHFolder'},
76+
'SWASH': 'MySWASHFolder',
77+
'HOS_ocean': 'MyHOS_oceanFolder',
78+
'WW3': 'MyWW3Folder',
79+
},
6780
'ext': { 'SWAN': 'swn',
68-
'WW3': 'inp',
81+
'WW3': 'nml',
6982
'SWASH': 'sws',
83+
'HOS_ocean': 'dat',
7084
'General': 'txt'}
7185
}
7286

0 commit comments

Comments
 (0)