Source code for opentidalfarm.tidal
from dolfin import Expression # Keep readthedocs happy
from dolfin import *
from dolfin_adjoint import *
# this imports NetCDFFile from netCDF4, Scientific.IO.NetCDF or scipy.io.netcdf (whichever is available)
from uptide.netcdf_reader import NetCDFFile
import utm
import uptide
import uptide.tidal_netcdf
import datetime
import scipy.interpolate
import numpy
__all__ = ["TidalForcing", "BathymetryDepthExpression"]
# We need to store tnci_time as a non-class variable, otherwise
# dolfin-adjoint tries to be clever and restores its values during the
# adjoint runs which yields an wrong behaviour for
# the "tnci_time != self.t" statement below
tnci_time = None
[docs]
class TidalForcing(Expression):
"""Create a TidalForcing Expression from OTPSnc NetCDF files, where
the grid is stored in a separate file (with "lon_z", "lat_z" and "mz"
fields). The actual data is read from a seperate file with hRe and hIm
fields.
The parameters are:
grid_file_name
data_file_name
ranges
utm_zone
utm_band
initial_time
constituents
"""
def __init__(self, **kwargs):
self.t = 0
self.utm_zone = kwargs["utm_zone"]
self.utm_band = kwargs["utm_band"]
tide = uptide.Tides(kwargs["constituents"])
tide.set_initial_time(kwargs["initial_time"])
self.tnci = uptide.tidal_netcdf.OTPSncTidalInterpolator(tide,
kwargs["grid_file_name"], kwargs["data_file_name"], kwargs["ranges"])
[docs]
def eval(self, values, X):
""" Evaluates the tidal forcing. """
global tnci_time
if tnci_time != self.t:
log(INFO, "Setting tidal forcing time to %f " % self.t)
self.tnci.set_time(float(self.t))
tnci_time = self.t
latlon = utm.to_latlon(X[0], X[1], self.utm_zone, self.utm_band)
try:
# OTPS has lon, lat coordinates!
values[0] = self.tnci.get_val((latlon[1], latlon[0]), allow_extrapolation=True)
except uptide.netcdf_reader.CoordinateError:
# uptide raises a CoordinateError if interpolated within the land mask, this shouldn't happen
# but dolfin evaluates too many points in the interior of the domain which are then not used
# but some of those might overlap with landmask, therefore set to NaN instead of raising an exception
# so that /if/ the value is used we'll notice it
values[0] = numpy.NaN
[docs]
class BathymetryDepthExpression(Expression):
"""Create a bathymetry depth Expression from a lat/lon NetCDF file, where
the depth values stored as "z" field.
Parameters are:
filename
utm_zone
utm_band
maxval. Default value is 10
domain. Default value is None
"""
def __init__(self, *args, **kwargs):
self._domain = kwargs.get("domain", None)
nc = NetCDFFile(kwargs["filename"], 'r')
lat = nc.variables['lat']
lon = nc.variables['lon']
values = nc.variables['z']
# work around incompatibilities in different netcdf libraries
if hasattr(lat, 'data'): lat = lat.data
if hasattr(lon, 'data'): lon = lon.data
if hasattr(values, 'data'): values = values.data
self.utm_zone = kwargs["utm_zone"]
self.utm_band = kwargs["utm_band"]
self.maxval = kwargs.get("maxval", 10)
self.interpolator = scipy.interpolate.RectBivariateSpline(lat, lon, values)
[docs]
def eval(self, values, x):
""" Evaluates the bathymetry at a point. """
lat, lon = utm.to_latlon(x[0], x[1], self.utm_zone, self.utm_band)
values[0] = max(self.maxval, -self.interpolator(lat, lon))