def __init__(self, function_space, wind_stress_field, atm_pressure_field, to_latlon, ncfile_pattern, init_date, target_coordsys, verbose=False): """ :arg function_space: Target (scalar) :class:`FunctionSpace` object onto which data will be interpolated. :arg wind_stress_field: A 2D vector :class:`Function` where the output wind stress will be stored. :arg atm_pressure_field: A 2D scalar :class:`Function` where the output atmospheric pressure will be stored. :arg to_latlon: Python function that converts local mesh coordinates to latitude and longitude: 'lat, lon = to_latlon(x, y)' :arg ncfile_pattern: A file name pattern for reading the atmospheric model output files. E.g. 'forcings/nam_air.local.2006_*.nc' :arg init_date: A :class:`datetime` object that indicates the start date/time of the Thetis simulation. Must contain time zone. E.g. 'datetime(2006, 5, 1, tzinfo=pytz.utc)' :arg target_coordsys: coordinate system in which the model grid is defined. This is used to rotate vectors to local coordinates. :kwarg bool verbose: Se True to print debug information. """ self.function_space = function_space self.wind_stress_field = wind_stress_field self.atm_pressure_field = atm_pressure_field # construct interpolators self.grid_interpolator = interpolation.NetCDFLatLonInterpolator2d( self.function_space, to_latlon) self.reader = interpolation.NetCDFSpatialInterpolator( self.grid_interpolator, ['uwind', 'vwind', 'prmsl']) self.timesearch_obj = interpolation.NetCDFTimeSearch(ncfile_pattern, init_date, ATMNetCDFTime, verbose=verbose) self.time_interpolator = interpolation.LinearTimeInterpolator( self.timesearch_obj, self.reader) lon = self.grid_interpolator.mesh_lonlat[:, 0] lat = self.grid_interpolator.mesh_lonlat[:, 1] self.vect_rotator = coordsys.VectorCoordSysRotation( coordsys.LL_WGS84, target_coordsys, lon, lat)
def __init__(self, function_space, wind_stress_field, atm_pressure_field, ncfile_pattern, init_date): self.function_space = function_space self.wind_stress_field = wind_stress_field self.atm_pressure_field = atm_pressure_field # construct interpolators self.grid_interpolator = interpolation.NetCDFLatLonInterpolator2d( self.function_space, to_latlon) self.reader = interpolation.NetCDFSpatialInterpolator( self.grid_interpolator, ['uwind', 'vwind', 'prmsl']) self.timesearch_obj = interpolation.NetCDFTimeSearch( ncfile_pattern, init_date, WRFNetCDFTime) self.time_interpolator = interpolation.LinearTimeInterpolator( self.timesearch_obj, self.reader) lon = self.grid_interpolator.mesh_lonlat[:, 0] lat = self.grid_interpolator.mesh_lonlat[:, 1] self.vect_rotator = coordsys.VectorCoordSysRotation( coordsys.LL_WGS84, coordsys.SPCS_N_OR, lon, lat)
def __init__(self, elev_field, init_date, to_latlon, target_coordsys, uv_field=None, constituents=None, boundary_ids=None, data_dir=None): """ :arg elev_field: Function where tidal elevation will be interpolated. :arg init_date: Datetime object defining the simulation init time. :arg to_latlon: Python function that converts local mesh coordinates to latitude and longitude: 'lat, lon = to_latlon(x, y)' :arg target_coordsys: coordinate system in which the model grid is defined. This is used to rotate vectors to local coordinates. :kwarg uv_field: Function where tidal transport will be interpolated. :kwarg constituents: list of tidal constituents, e.g. ['M2', 'K1'] :kwarg boundary_ids: list of boundary_ids where tidal data will be evaluated. If not defined, tides will be in evaluated in the entire domain. :kward data_dir: path to directory where tidal model netCDF files are located. """ assert init_date.tzinfo is not None, 'init_date must have time zone information' if constituents is None: constituents = ['Q1', 'O1', 'P1', 'K1', 'N2', 'M2', 'S2', 'K2'] self.data_dir = data_dir if data_dir is not None else '' if not self.compute_velocity and uv_field is not None: warning( '{:}: uv_field is defined but velocity computation is not supported. uv_field will be ignored.' .format(__class__.__name__)) self.compute_velocity = self.compute_velocity and uv_field is not None # determine nodes at the boundary self.elev_field = elev_field self.uv_field = uv_field fs = elev_field.function_space() if boundary_ids is None: # interpolate in the whole domain self.nodes = np.arange( self.elev_field.dat.data_with_halos.shape[0]) else: bc = DirichletBC(fs, 0., boundary_ids, method='geometric') self.nodes = bc.nodes self._empty_set = self.nodes.size == 0 xy = SpatialCoordinate(fs.mesh()) fsx = Function(fs).interpolate(xy[0]).dat.data_ro_with_halos fsy = Function(fs).interpolate(xy[1]).dat.data_ro_with_halos if not self._empty_set: latlon = [] for node in self.nodes: x, y = fsx[node], fsy[node] lat, lon = to_latlon(x, y, positive_lon=True) latlon.append((lat, lon)) self.latlon = np.array(latlon) # compute bounding box bounds_lat = [self.latlon[:, 0].min(), self.latlon[:, 0].max()] bounds_lon = [self.latlon[:, 1].min(), self.latlon[:, 1].max()] if self.coord_layout == 'lon,lat': self.ranges = (bounds_lon, bounds_lat) else: self.ranges = (bounds_lat, bounds_lon) self.tide = uptide.Tides(constituents) self.tide.set_initial_time(init_date) self._create_readers() if self.compute_velocity: lat = self.latlon[:, 0] lon = self.latlon[:, 1] self.vect_rotator = coordsys.VectorCoordSysRotation( coordsys.LL_WGS84, target_coordsys, lon, lat)
def __init__(self, function_space_2d, function_space_3d, fields, field_names, field_fnstr, to_latlon, basedir, file_pattern, init_date, target_coordsys, verbose=False): """ :arg function_space_2d: Target (scalar) :class:`FunctionSpace` object onto which 2D data will be interpolated. :arg function_space_3d: Target (scalar) :class:`FunctionSpace` object onto which 3D data will be interpolated. :arg fields: list of :class:`Function` objects where data will be stored. :arg field_names: List of netCDF variable names for the fields. E.g. ['Salinity', 'Temperature']. :arg field_fnstr: List of variables in netCDF file names. E.g. ['s3d', 't3d']. :arg to_latlon: Python function that converts local mesh coordinates to latitude and longitude: 'lat, lon = to_latlon(x, y)' :arg basedir: Root dir where NCOM files are stored. E.g. '/forcings/ncom'. :arg file_pattern: A file name pattern for reading the NCOM output files (excluding the basedir). E.g. {year:04d}/{fieldstr:}/{fieldstr:}.glb8_2f_{year:04d}{month:02d}{day:02d}00.nc'. :arg init_date: A :class:`datetime` object that indicates the start date/time of the Thetis simulation. Must contain time zone. E.g. 'datetime(2006, 5, 1, tzinfo=pytz.utc)' :arg target_coordsys: coordinate system in which the model grid is defined. This is used to rotate vectors to local coordinates. :kwarg bool verbose: Se True to print debug information. """ self.function_space_2d = function_space_2d self.function_space_3d = function_space_3d for f in fields: assert f.function_space() in [ self.function_space_2d, self.function_space_3d ], 'field \'{:}\' does not belong to given function space.'.format( f.name()) assert len(fields) == len(field_names) assert len(fields) == len(field_fnstr) self.field_names = field_names self.fields = dict(zip(self.field_names, fields)) # construct interpolators self.grid_interpolator_2d = SpatialInterpolatorNCOM2d( self.function_space_2d, to_latlon, basedir) self.grid_interpolator_3d = SpatialInterpolatorNCOM3d( self.function_space_3d, to_latlon, basedir) # each field is in different file # construct time search and interp objects separately for each self.time_interpolator = {} for ncvarname, fnstr in zip(field_names, field_fnstr): gi = self.grid_interpolator_2d if fnstr == 'ssh' else self.grid_interpolator_3d r = interpolation.NetCDFSpatialInterpolator(gi, [ncvarname]) pat = file_pattern.replace('{fieldstr:}', fnstr) pat = os.path.join(basedir, pat) ts = interpolation.DailyFileTimeSearch(pat, init_date, verbose=verbose) ti = interpolation.LinearTimeInterpolator(ts, r) self.time_interpolator[ncvarname] = ti # construct velocity rotation object self.rotate_velocity = ('U_Velocity' in field_names and 'V_Velocity' in field_names) self.scalar_field_names = list(self.field_names) if self.rotate_velocity: self.scalar_field_names.remove('U_Velocity') self.scalar_field_names.remove('V_Velocity') lat = self.grid_interpolator_3d.latlonz_array[:, 0] lon = self.grid_interpolator_3d.latlonz_array[:, 1] self.vect_rotator = coordsys.VectorCoordSysRotation( coordsys.LL_WGS84, target_coordsys, lon, lat)
def test(): mesh2d = Mesh('mesh_cre-plume002.msh') comm = mesh2d.comm p1 = FunctionSpace(mesh2d, 'CG', 1) p1v = VectorFunctionSpace(mesh2d, 'CG', 1) windstress_2d = Function(p1v, name='wind stress') atmpressure_2d = Function(p1, name='atm pressure') sim_tz = timezone.FixedTimeZone(-8, 'PST') init_date = datetime.datetime(2015, 5, 16, tzinfo=sim_tz) pattern = 'forcings/atm/wrf/wrf_air.2015_*_*.nc' wrf = WRFInterpolator(p1, windstress_2d, atmpressure_2d, pattern, init_date) # create a naive interpolation for first file xy = SpatialCoordinate(p1.mesh()) fsx = Function(p1).interpolate(xy[0]).dat.data_with_halos fsy = Function(p1).interpolate(xy[1]).dat.data_with_halos mesh_lonlat = [] for node in range(len(fsx)): lat, lon = to_latlon(fsx[node], fsy[node]) mesh_lonlat.append((lon, lat)) mesh_lonlat = np.array(mesh_lonlat) ncfile = netCDF4.Dataset('forcings/atm/wrf/wrf_air.2015_05_16.nc') itime = 10 grid_lat = ncfile['lat'][:].ravel() grid_lon = ncfile['lon'][:].ravel() grid_lonlat = np.array((grid_lon, grid_lat)).T grid_pres = ncfile['prmsl'][itime, :, :].ravel() pres = scipy.interpolate.griddata(grid_lonlat, grid_pres, mesh_lonlat, method='linear') grid_uwind = ncfile['uwind'][itime, :, :].ravel() uwind = scipy.interpolate.griddata(grid_lonlat, grid_uwind, mesh_lonlat, method='linear') grid_vwind = ncfile['vwind'][itime, :, :].ravel() vwind = scipy.interpolate.griddata(grid_lonlat, grid_vwind, mesh_lonlat, method='linear') vrot = coordsys.VectorCoordSysRotation(coordsys.LL_WGS84, coordsys.SPCS_N_OR, mesh_lonlat[:, 0], mesh_lonlat[:, 1]) uwind, vwind = vrot(uwind, vwind) u_stress, v_stress = compute_wind_stress(uwind, vwind) # compare wrf.set_fields((itime - 8) * 3600.) # NOTE timezone offset assert np.allclose(pres, atmpressure_2d.dat.data_with_halos) assert np.allclose(u_stress, windstress_2d.dat.data_with_halos[:, 0]) # write fields to disk for visualization out_pres = File('tmp/atm_pressure.pvd') out_wind = File('tmp/wind_stress.pvd') hours = 24 * 3 granule = 4 simtime = np.arange(granule * hours) * 3600. / granule i = 0 for t in simtime: wrf.set_fields(t) norm_atm = norm(atmpressure_2d) norm_wind = norm(windstress_2d) if comm.rank == 0: print('{:} {:} {:} {:}'.format(i, t, norm_atm, norm_wind)) out_pres.write(atmpressure_2d) out_wind.write(windstress_2d) i += 1
def test(): """ Tests atmospheric model data interpolation. .. note:: The following files must be present forcings/atm/wrf/wrf_air.2015_05_16.nc forcings/atm/wrf/wrf_air.2015_05_17.nc forcings/atm/nam/nam_air.local.2006_05_01.nc forcings/atm/nam/nam_air.local.2006_05_02.nc """ mesh2d = Mesh('mesh_cre-plume_03_normal.msh') comm = mesh2d.comm p1 = get_functionspace(mesh2d, 'CG', 1) p1v = get_functionspace(mesh2d, 'CG', 1, vector=True) windstress_2d = Function(p1v, name='wind stress') atmpressure_2d = Function(p1, name='atm pressure') sim_tz = timezone.FixedTimeZone(-8, 'PST') # WRF # init_date = datetime.datetime(2015, 5, 16, tzinfo=sim_tz) # pattern = 'forcings/atm/wrf/wrf_air.2015_*_*.nc' # atm_time_step = 3600. # for verification only # test_atm_file = 'forcings/atm/wrf/wrf_air.2015_05_16.nc' # NAM init_date = datetime.datetime(2006, 5, 1, tzinfo=sim_tz) pattern = 'forcings/atm/nam/nam_air.local.2006_*_*.nc' atm_time_step = 3*3600. test_atm_file = 'forcings/atm/nam/nam_air.local.2006_05_01.nc' atm_interp = ATMInterpolator(p1, windstress_2d, atmpressure_2d, to_latlon, pattern, init_date, COORDSYS, verbose=True) # create a naive interpolation for first file xy = SpatialCoordinate(p1.mesh()) fsx = Function(p1).interpolate(xy[0]).dat.data_with_halos fsy = Function(p1).interpolate(xy[1]).dat.data_with_halos mesh_lonlat = [] for node in range(len(fsx)): lat, lon = to_latlon(fsx[node], fsy[node]) mesh_lonlat.append((lon, lat)) mesh_lonlat = np.array(mesh_lonlat) ncfile = netCDF4.Dataset(test_atm_file) itime = 6 grid_lat = ncfile['lat'][:].ravel() grid_lon = ncfile['lon'][:].ravel() grid_lonlat = np.array((grid_lon, grid_lat)).T grid_pres = ncfile['prmsl'][itime, :, :].ravel() pres = scipy.interpolate.griddata(grid_lonlat, grid_pres, mesh_lonlat, method='linear') grid_uwind = ncfile['uwind'][itime, :, :].ravel() uwind = scipy.interpolate.griddata(grid_lonlat, grid_uwind, mesh_lonlat, method='linear') grid_vwind = ncfile['vwind'][itime, :, :].ravel() vwind = scipy.interpolate.griddata(grid_lonlat, grid_vwind, mesh_lonlat, method='linear') vrot = coordsys.VectorCoordSysRotation(coordsys.LL_WGS84, COORDSYS, mesh_lonlat[:, 0], mesh_lonlat[:, 1]) uwind, vwind = vrot(uwind, vwind) u_stress, v_stress = compute_wind_stress(uwind, vwind) # compare atm_interp.set_fields(itime*atm_time_step - 8*3600.) # NOTE timezone offset assert np.allclose(pres, atmpressure_2d.dat.data_with_halos) assert np.allclose(u_stress, windstress_2d.dat.data_with_halos[:, 0]) # write fields to disk for visualization pres_fn = 'tmp/AtmPressure2d.pvd' wind_fn = 'tmp/WindStress2d.pvd' print('Saving output to {:} {:}'.format(pres_fn, wind_fn)) out_pres = File(pres_fn) out_wind = File(wind_fn) hours = 24*1.5 granule = 4 simtime = np.arange(granule*hours)*3600./granule i = 0 for t in simtime: atm_interp.set_fields(t) norm_atm = norm(atmpressure_2d) norm_wind = norm(windstress_2d) if comm.rank == 0: print('{:} {:} {:} {:}'.format(i, t, norm_atm, norm_wind)) out_pres.write(atmpressure_2d) out_wind.write(windstress_2d) i += 1