|
| 1 | +from datetime import datetime |
| 2 | +from os import path as op |
| 3 | + |
1 | 4 | import numpy as np |
2 | 5 | import pandas as pd |
3 | 6 | import xarray as xr |
| 7 | +from scipy.interpolate import RegularGridInterpolator |
4 | 8 |
|
5 | 9 | from ..core.geo import geo_distance_cartesian, geodesic_distance |
6 | 10 |
|
@@ -209,3 +213,147 @@ def vortex_model_grid( |
209 | 213 | }, |
210 | 214 | coords={ylab: cg_lat, xlab: cg_lon, "time": times}, |
211 | 215 | ) |
| 216 | + |
| 217 | + |
| 218 | +def vortex2delft_3D_FM_nc( |
| 219 | + mesh: xr.Dataset, |
| 220 | + ds_vortex: xr.Dataset, |
| 221 | + path_output: str, |
| 222 | + ds_name: str = "forcing_Tonga_vortex.nc", |
| 223 | + fotcing_ext: str = "GreenSurge_GFDcase_wind.ext", |
| 224 | +) -> None: |
| 225 | + """ |
| 226 | + Convert the vortex dataset to a Delft3D FM compatible netCDF forcing file. |
| 227 | +
|
| 228 | + Parameters |
| 229 | + ---------- |
| 230 | + mesh : xarray.Dataset |
| 231 | + The mesh dataset containing the node coordinates. |
| 232 | + ds_vortex : xarray.Dataset |
| 233 | + The vortex dataset containing wind speed and pressure data. |
| 234 | + path_output : str |
| 235 | + The output path where the netCDF file will be saved. |
| 236 | + ds_name : str |
| 237 | + The name of the output netCDF file, default is "forcing_Tonga_vortex.nc". |
| 238 | + forcing_ext : str |
| 239 | + The extension for the forcing file, default is "GreenSurge_GFDcase_wind.ext". |
| 240 | + """ |
| 241 | + longitude = mesh.mesh2d_node_x.values |
| 242 | + latitude = mesh.mesh2d_node_y.values |
| 243 | + n_time = ds_vortex.time.size |
| 244 | + |
| 245 | + lat_interp = xr.DataArray(latitude, dims="node") |
| 246 | + lon_interp = xr.DataArray(longitude, dims="node") |
| 247 | + |
| 248 | + angle = np.deg2rad((270 - ds_vortex.Dir.values) % 360) |
| 249 | + W = ds_vortex.W.values |
| 250 | + |
| 251 | + ds_vortex_interp = ds_vortex.drop_vars(["Dir", "W"]) |
| 252 | + |
| 253 | + ds_vortex_interp["windx"] = ( |
| 254 | + ( |
| 255 | + "lat", |
| 256 | + "lon", |
| 257 | + "time", |
| 258 | + ), |
| 259 | + (W * np.cos(angle)).astype(np.float32), |
| 260 | + ) |
| 261 | + ds_vortex_interp["windy"] = ( |
| 262 | + ("lat", "lon", "time"), |
| 263 | + (W * np.sin(angle)).astype(np.float32), |
| 264 | + ) |
| 265 | + |
| 266 | + ds_vortex_interp = ds_vortex_interp.rename( |
| 267 | + { |
| 268 | + "p": "airpressure", |
| 269 | + "lon": "longitude", |
| 270 | + "lat": "latitude", |
| 271 | + } |
| 272 | + ) |
| 273 | + |
| 274 | + forcing_dataset = ds_vortex_interp.interp( |
| 275 | + latitude=lat_interp, longitude=lon_interp |
| 276 | + ).transpose("time", "node") |
| 277 | + forcing_dataset["time"] = np.arange(n_time) |
| 278 | + |
| 279 | + reference_date_str = ( |
| 280 | + ds_vortex.time.values[0] |
| 281 | + .astype("M8[ms]") |
| 282 | + .astype(datetime) |
| 283 | + .strftime("%Y-%m-%d %H:%M:%S") |
| 284 | + ) |
| 285 | + |
| 286 | + forcing_dataset["windx"].attrs = { |
| 287 | + "coordinates": "time node", |
| 288 | + "long_name": "Wind speed in x direction", |
| 289 | + "standard_name": "windx", |
| 290 | + "units": "m s-1", |
| 291 | + } |
| 292 | + forcing_dataset["windy"].attrs = { |
| 293 | + "coordinates": "time node", |
| 294 | + "long_name": "Wind speed in y direction", |
| 295 | + "standard_name": "windy", |
| 296 | + "units": "m s-1", |
| 297 | + } |
| 298 | + |
| 299 | + forcing_dataset["airpressure"].attrs = { |
| 300 | + "coordinates": "time node", |
| 301 | + "long_name": "Atmospheric Pressure", |
| 302 | + "standard_name": "air_pressure", |
| 303 | + "units": "Pa", |
| 304 | + } |
| 305 | + |
| 306 | + forcing_dataset["time"].attrs = { |
| 307 | + "standard_name": "time", |
| 308 | + "long_name": f"Time - hours since {reference_date_str} +00:00", |
| 309 | + "time_origin": f"{reference_date_str}", |
| 310 | + "units": f"hours since {reference_date_str} +00:00", |
| 311 | + "calendar": "gregorian", |
| 312 | + "description": "Time definition for the forcing data", |
| 313 | + } |
| 314 | + |
| 315 | + forcing_dataset["longitude"].attrs = { |
| 316 | + "description": "Longitude of each mesh node of the computational grid", |
| 317 | + "standard_name": "longitude", |
| 318 | + "long_name": "longitude", |
| 319 | + "units": "degrees_east", |
| 320 | + } |
| 321 | + forcing_dataset["latitude"].attrs = { |
| 322 | + "description": "Latitude of each mesh node of the computational grid", |
| 323 | + "standard_name": "latitude", |
| 324 | + "long_name": "latitude", |
| 325 | + "units": "degrees_north", |
| 326 | + } |
| 327 | + |
| 328 | + forcing_dataset.to_netcdf( |
| 329 | + op.join(path_output, ds_name), |
| 330 | + "w", |
| 331 | + "NETCDF3_CLASSIC", |
| 332 | + unlimited_dims="time", |
| 333 | + ) |
| 334 | + |
| 335 | + texte = f"""QUANTITY=windx |
| 336 | +FILENAME={ds_name} |
| 337 | +VARNAME =windx |
| 338 | +FILETYPE=11 |
| 339 | +METHOD=3 |
| 340 | +OPERAND=O |
| 341 | +
|
| 342 | +QUANTITY=windy |
| 343 | +FILENAME={ds_name} |
| 344 | +VARNAME =windy |
| 345 | +FILETYPE=11 |
| 346 | +METHOD=3 |
| 347 | +OPERAND=O |
| 348 | +
|
| 349 | +QUANTITY=airpressure |
| 350 | +FILENAME={ds_name} |
| 351 | +VARNAME =airpressure |
| 352 | +FILETYPE=11 |
| 353 | +METHOD=3 |
| 354 | +OPERAND=O""" |
| 355 | + |
| 356 | + with open( |
| 357 | + op.join(path_output, fotcing_ext), "w", encoding="utf-8" |
| 358 | + ) as f: |
| 359 | + f.write(texte) |
0 commit comments