Professional Documents
Culture Documents
com/pwcazenave/fvcom-toolbox
Add new example script to create inputs for FVCOM (including heating,
master
20150319
> https://gitlab.em.pml.ac.uk/pica/fvcom-toolbox or
+%
> https://github.com/pwcazenave/fvcom-toolbox
> http://polaris.esr.org/ptm_index.html
> http://woodshole.er.usgs.gov/operations/sea-mat/air_sea-html
+%
+% Author(s):
+% Pierre Cazenave (Plymouth Marine Laboratory)
+%
+% Revision history:
+%
+% 2015-03-19 Example script to generate FVCOM inputs from TPXO, NCEP and
+% HYCOM data sources.
+
+matlabrc
+close all
+clc
+
+global ftbverbose
+ftbverbose = 1; % be noisy
+
+addpath('/users/modellers/pica/Code/fvcom-toolbox/utilities')
+addpath('/users/modellers/pica/Code/fvcom-toolbox/fvcom_prepro/')
+addpath('/users/modellers/rito/matlab/air-sea')
+conf.obc_tides.dirTMD = '/users/modellers/pica/Code/MATLAB/toolboxes/TMD2.03/';
+addpath(conf.obc_tides.dirTMD)
+addpath(fullfile(conf.obc_tides.dirTMD, 'FUNCTIONS'))
+
+%%%-----------------------------------------------------------------------+%%%
INPUT CONFIGURATION
+%%%
+%%% Define the model input parameters here e.g. number of tidal components,
+%%% type of boundary forcing, estimated current velocity, sponge layer
+%%% coefficient and radius, forcing source etc.
+%%%
+%%%-----------------------------------------------------------------------+
+conf.base = '/data/medusa/pica/models/FVCOM/pml-tamar/run/';
+
+% Which version of FVCOM are we using (for the forcing file formats)?
+conf.FVCOM_version = '3.2.1';
+
+%%%-----------------------------------------------------------------------+%%%
Time stuf
+%%%-----------------------------------------------------------------------+
+% Model time ([Y, M, D, h, m, s])
+conf.modelYear = 2013;
+conf.startDate = [conf.modelYear, 10, 01, 00, 00, 00];
+conf.endDate = [conf.modelYear, 11, 01, 00, 00, 00];
+
+% Time sampling for the surface forcing interpolation and the open boundary
+% tidal forcing (in hours)
+conf.sampling.surface = 1;
+conf.sampling.tides = 5 / 60;
+
+%%%-----------------------------------------------------------------------+%%%
Spatial stuf
+%%%-----------------------------------------------------------------------+
+% Case name for the model inputs and outputs
+conf.casename = 'grid_v01';
+
+conf.coordType = 'cartesian'; % 'cartesian' or 'spherical'
+% Input grid UTM Zone (if applicable)
+conf.utmZone = {'30 U'}; % syntax for utm2deg
+
+% Option to smooth the bathymetry data.
+conf.smoothBathy = 'no'; % 'yes' or 'no'.
+if strcmpi(conf.smoothBathy, 'yes')
+
+end
+
+% Sigma layer definition file.
+conf.sigma_file = fullfile(conf.base, 'input/configs/', conf.casename, 'sigma_geom.dat');
+
+% Give some names to the boundaries. This must match the number of node
+% strings defined in SMS. Ideally, the order of the names should match the
+% order in which the boundaries were made in SMS.
+conf.boundaryNames = {'South'}; % Number is (relatively) important!
+
+%%%-----------------------------------------------------------------------+%%%
+%%%-----------------------------------------------------------------------+
+% Type of open boundary treatment (see Table 6.1 (?) in the manual).
+% 1 - Active (ASL): sea level is specified at the open boundary.
+% 2 - Clamped: zeta = 0 at the boundary (Beardsley and Haidvogel, 1981).
+% 3 - Implicit Gravity Wave Radiation.
+% 4 - Partial Clamped Gravity Wave Radiation (Blumberg and Kantha, 1985).
+% 5 - Explicit Orlanski Radiation (Orlanski, 1976; Chapman, 1985)
+conf.obc_type = 1;
+
+% Sponge layer parameters
+conf.sponge.radius = 10000; % in metres
+conf.sponge.coef = 0.0001;
+
+% z0 value in metres (for uniform) or 'random'.
+conf.bedRoughness = 0.03; % or 0.015, 0.025 or 0.03 - Davies and Furnes (1980) shelf model
+
+% Estimated velocity (m/s) and tidal range (m) for time step estimate
+conf.estVel = 2;
+conf.estRange = 5;
+
+%%%-----------------------------------------------------------------------+%%%
+%%%------------------------------------------------------------------------
+
+% Open boundary nodal forcing type
+% 'z' for predicted surface elevation
+% 'fvcom' for FVCOM modelled surface elevation
+% 'phase-amp' for amplitudes and phases
+% 'polpred' for POLPRED amplitudes and phases
+conf.obc_tides.forcing = 'z';
+
+% How many tidal constituents do we actually want to use at the model
+% boundaries? Case sensitive (M2 != m2).
+% conf.obc_tides.components = {'M2','S2','N2','K2','K1','O1','P1','Q1','Mf','Mm','Ssa','M4','MS4','MN4
+conf.obc_tides.components = {'M2','S2','N2','K2','K1','O1','P1','Q1','M4'};
+
+% Location of the TMD model description file. Fall back to the global model
+% if the regional model doesn't work (NaNs in the time series). Regional
+% model used here is one suggested by Dima Aleynik and requires
+% ftp://ftp.oce.orst.edu/dist/tides/regional/ES.tar.Z to be extracted into
+% the TMD toolbox directory.
+conf.obc_tides.globalModel = '/users/modellers/pica/Code/MATLAB/toolboxes/TMD2.03/DATA/Model_
+conf.obc_tides.model = '/users/modellers/pica/Code/MATLAB/toolboxes/TMD2.03/DATA/Model_ES20
+
+%%%-----------------------------------------------------------------------+%%%
+%%%-----------------------------------------------------------------------+
+% Open boundary temperatures (string for source or number for constant).
+conf.obc_temp = 'HYCOM';
+
+% Open boundary salinities (string for source or number for constant).
+conf.obc_salt = 'HYCOM';
+
+% Surface heat fluxes.
+conf.surface_heat = 'NCEP';
+
+%%%-----------------------------------------------------------------------+%%%
+%%%-----------------------------------------------------------------------+
+%% Process the files needed for all months.
+
+% Read the input mesh and bathymetry. Also creates the data necessary for
+% the Coriolis correction in FVCOM.
+Mobj = read_sms_mesh(...
+
+
+% Clean out some unneeded fields.
+Mobj = rmfield(Mobj, 'riv_nodes');
+
+% Add grid metrics.
+Mobj = setup_metrics(Mobj);
+
+% Smooth the bathymetry if desired.
Mobj = setup_metrics(Mobj);
conf.smoothFactors(1), conf.smoothFactors(2));
% Mobj.h = smoothfield2(Mobj.h,Mobj,inputconf.smoothFactors(2));
+end
+
+% Create a Coriolis file from the bathy which varies with latitude. Given
+% the size of the domain, this is probably necessary. First need to convert
+% the UTM coordinates to lat/long to be able to calculate it, if
+% appropriate.
+if Mobj.have_lonlat == 0
+
Mobj.have_lonlat = true;
clear utmZones
+end
+Mobj = add_coriolis(Mobj, 'uselatitude');
+
+% Parse the open boundary nodes and add accordingly.
+if Mobj.have_strings
+
for i = 1:size(Mobj.read_obc_nodes, 2)
nodeList = double(cell2mat(Mobj.read_obc_nodes(i)));
end
clear nodeList
+end
+
+% Create a sponge layer
+if Mobj.have_strings
+
for i = 1:size(Mobj.read_obc_nodes, 2)
nodeList = double(cell2mat(Mobj.read_obc_nodes(i)));
Mobj = add_sponge_nodes_list(Mobj,nodeList,...
conf.sponge.coef);
+
+
clear nodeList
end
+end
+else
+
fprintf('Unrecognised bed roughness type.\nSpecify a size (in m) or ''random'' for random bed ro
+end
+
+% Estimate model time step. Supply estimated velocity (m/s) and tidal
+% range (m) after the mesh object.
+Mobj = estimate_ts(Mobj, conf.estVel, conf.estRange);
+
+%% Prepare the data in month long sections and output to subdirectories
+
+% Create an array of the days in this year's months to use to get the
+% Modified Julian Day start and end date range.
+daysOfMonths = eomday(conf.modelYear, 1:12);
+% Get the MJD of the start of the year. We'll use this within the loop to
+% add the days of the months we're iterating through.
+s0 = greg2mjulian(conf.modelYear, 1, 1, 0, 0, 0);
+
+for mm = conf.startDate(2):conf.endDate(2)
+
% Get the Modified Julian Day for the start and end of the month we're
% on at the moment. We'll pad by few days either way to give us a bit
% of leeway. Only do this within the year (i.e. don't pad to before the
% start of the year because the get_NCEP_forcing script can't get data
if mm == 1
+
+
+
+
+
end
conf.time.tides = ...
conf.startDateMJD:conf.sampling.tides/24:conf.endDateMJD;
clear sYr sMon sDay sHr sMin sSec eYr eMon eDay eHr eMin eSec
+
+
% Output directory will contain some duplicate files (e.g. model grid
% etc.) but this makes things easier to manage. One subdirectory per
'input/configs/', ...
conf.casename, ...
if exist(conf.outbase, 'dir') ~= 7
+
+
mkdir(conf.outbase)
end
+
+
% range.
+
+
oldDir = pwd;
+
+
Mobj.Components = conf.obc_tides.components;
+
+
conf.time.tidesMJD = datenum(...
conf.startDateMJD):...
conf.sampling.tides/24:...
datenum(conf.endDateMJD);
+
+
+
+
+
+
+
% TPXO constituents is M2, S2, N2, K2, K1, O1, P1, Q1, MF, MM, M4,
% MS4, MN4.
tpxoConsts = {'M2', 'S2', 'N2', 'K2', 'K1', 'O1', 'P1', 'Q1', ...
tInd = 1:length(tpxoConsts);
for i=1:length(Mobj.Components)
if ~isempty(tPos)
tIndUse(i) = tPos;
else
end
end
% Tidy up a bit
tIndUse = tIndUse(~isnan(tIndUse));
+
+
% Since I'm likely to have many more time steps than locations,
% should reflect the order of the open boundary node IDs as FVCOM
% assumes they just map directly. So, rather than iterate through
% boundary).
tmpObcNodes = Mobj.obc_nodes';
ObcNodes = tmpObcNodes(tmpObcNodes~=0)';
clear tmpObcNodes
parfor i = 1:size(ObcNodes, 2)
currLon = Mobj.lon(ObcNodes(i));
currLat = Mobj.lat(ObcNodes(i));
if ftbverbose
end
tmd_tide_pred(conf.obc_tides.model, ...
'z', tIndUse);
if isnan(surfaceElevation(i, :))
tmd_tide_pred(conf.obc_tides.globalModel, ...
'z', tIndUse);
end
end
Mobj.surfaceElevation = surfaceElevation;
+
+
+
+
+
+
end
+
+
try
% Grid
[conf.casename, '_grd.dat']));
+
+
% Bathymetry
[conf.casename, '_dep.dat']));
+
+
% Coriolis
[conf.casename, '_cor.dat']));
+
+
% Open boundaries
[conf.casename, '_obc.dat']))
+
+
% Sponge file
[conf.casename, '_spg.dat']))
+
+
+
+
[conf.casename,'_station.dat']));
+
+
% Sigma file
copyfile(conf.sigma_file, conf.outbase)
+
+
+
+
+
catch err
rethrow(err)
end
if strcmpi(conf.surface_heat, 'NCEP')
% following parameters:
% The script converts the NCEP data from the OPeNDAP server from
% Mobj.lat).
+
+
heating.domain_cols = length(heating.lon);
heating.domain_rows = length(heating.lat);
if isfield(heating, 'rhum')
heating.domain_cols_alt = length(heating.rhum.lon);
heating.domain_rows_alt = length(heating.rhum.lat);
end
+
+
if isfield(heating, 'rhum')
end
if isfield(heating, 'rhum')
end
+
+
+
+
if isfield(heating, 'rhum')
+
+
+
+
if isfield(heating, 'rhum')
end
+
+
tic
if ftbverbose
+
+
+
+
if exist('heating_interp', 'var')
conf.FVCOM_version);
end
+
+
end
+
+
+
+
+
+
% open boundaries.
+
+
Mobj.ts_times, ...
Mobj.temperature, ...
Mobj.salt,...
Mobj)
end
+
+
% Add the daily SSH data on top of the predicted tidal elevations if
+
+
+
+
if ~isfield(hycom, 'ssh')
warning('No sea surface height field in the HYCOM data.')
elseif isfield(hycom, 'ssh') && ~strcmpi(conf.obc_tides.forcing, 'fvcom')
% find the nearest point in the HYCOM grid (don't worry about
if Mobj.have_strings
tmpObcNodes = Mobj.obc_nodes';
+
+
fvlon = Mobj.lon(oNodes);
fvlat = Mobj.lat(oNodes);
+
+
% Loop through all the nodes and find the nearest SSH
% values.
+
+
for p = 1:length(fvlon)
fx = fvlon(p);
fy = fvlat(p);
cc = 0;
cc = cc + 1;
if cc > 100
error('Couldn''t find sea surface height value within the 100 nearest elements in t
end
end
+
+
+
+
% predicted.
end
end
end
+
+
+
+end