Home > utilities > grid2fvcom.m

grid2fvcom

PURPOSE ^

Interpolate regularly gridded surface forcing data onto a given FVCOM

SYNOPSIS ^

function fvcom = grid2fvcom(Mobj, vars, data, varargin)

DESCRIPTION ^

 Interpolate regularly gridded surface forcing data onto a given FVCOM
 grid.

 fvcom = grid2fvcom(Mobj,vars,data)

 DESCRIPTION:
   Takes a given NCEP reanalysis grid file and interpolates the U10 and
   V10 values onto the specified FVCOM grid file.

 INPUT:
   Mobj - MATLAB mesh object with the following fields:
       x, y, lon, lat - cartesian and spherical node coordinates. These
       are transferred to the NetCDF file only and are not used in the
       interpolation at all.
       nVerts - number of vertices (nodes) in the unstructured grid.
       nElems - number of elements in the unstructured grid.
   vars - a cell array of the variable names to be interpolated on the
       FVCOM grid in Mobj (e.g. uwnd, U10, vwnd, V10 etc.).
   data - a struct which contains the following arrays:
       x - x data (probably best in cartesian for the interpolation)
       y - y data (probably best in cartesian for the interpolation)
       The struct must also contain all the variables defined in vars.
       time - time vector (in Modified Julian Days). If you're using some
       of the NCEP surface products (e.g. relative humitidy, sea level
       pressure), you need to supply x and y coordinates for their grids
       as .xalt and .yalt).
   varargin - keyword/argument pairs:
       add_elems - true/false - set to true to enable interpolation of
       field onto both nodes and elements (defaults to both). This is
       useful if you run into memory issues as this can effectively halve
       the memory requirements.

 OUTPUT:
   fvcom - struct of the interpolated data values at the model nodes and
       element centres (unless add_elems is false, in which case, only
       nodes). Also includes any variables which were in the input struct
       but which have not been interpolated (e.g. time).

 EXAMPLE USAGE:
   interpfields = {'uwnd', 'vwnd', 'slp', 'nshf', 'nlwrs', 'nswrs', ...
       'P_E', 'Et', 'time', 'lon', 'lat', 'x', 'y'};
   forcing_interp = grid2fvcom(Mobj, interpfields, forcing);

 NOTE:
   The shape of the returned arrays for rhum and slp (via
   get_NCEP_forcing.m) have sometimes differed from the other vairables
   (they appear to be projected onto a different grid). Give data.xalt and
   data.yalt to specify the alternative grid.

 Author(s):
   Pierre Cazenave (Plymouth Marine Laboratory)

 Revision history:
   2012-10-15 First version based on ncep2fvcom_U10V10.m in the
   fvcom-toolbox.
   2012-10-16 Removed the code to read the NCEP file. Instead, farmed that
   out to a new function (read_NCEP_wind) so that the relevant section can
   be more readily extracted (rather than using the entire globe's data:
   it's easier to subsample and provide the subsampled data here).
   2012-10-17 Add outputs to the function for use in visualisation.
   2012-10-19 Add wind struct as input rather than separate u, v, time and
   lat/long arrays. Makes invocation a bit cleaner.
   2012-11-01 Farmed out the creation of the NetCDF file to
   write_FVCOM_forcing.m and made this purely an interpolation script.
   2013-02-14 Add support for interpolating data on two different grids
   through the .xalt and .yalt fields in the input data structure. This is
   handy if you've got data from NCEP from both the Surface and Gaussian
   products, each of which are on different grids.
   2013-05-16 Add parallel for loops if the Parallel Computing Toolbox is
   available (MATLAB parfor loops fail gracefully to for loops if it is
   not available, in which case no harm, no foul).
   2013-07-18 Add more elegant case statement rather than using string
   comparisons.
   2013-08-06 Fix fairly significant bug in which the position arrays were
   transposed relative to the data arrays. The code now checks for the
   dimensions and warns if they have been flipped to match. There is no
   checking that the flip has worked because the xalt and yalt arrays
   complicate things too much for me to figure out today. If you want to
   implement that functionality, please do so! I also added commented-out
   figure at the end to check the interpolation has worked properly,
   should you wish to check manually.
   2013-12-04 Check for the presence of the input fields being requested
   in the input struct to avoid finding out that the last field in vars
   doesn't exist in data. Change the way the alternative coordinate arrays
   are used to accommodate subtleties in the parallel code in MATLAB.
   2015-05-20 Update the parallel processing commands.
   2015-05-22 Add option to disable output on element centres.
   2016-06-02 Fix the alternative grid handling in the parallel loop.
   Remove commented out code too.

==========================================================================

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function fvcom = grid2fvcom(Mobj, vars, data, varargin)
0002 % Interpolate regularly gridded surface forcing data onto a given FVCOM
0003 % grid.
0004 %
0005 % fvcom = grid2fvcom(Mobj,vars,data)
0006 %
0007 % DESCRIPTION:
0008 %   Takes a given NCEP reanalysis grid file and interpolates the U10 and
0009 %   V10 values onto the specified FVCOM grid file.
0010 %
0011 % INPUT:
0012 %   Mobj - MATLAB mesh object with the following fields:
0013 %       x, y, lon, lat - cartesian and spherical node coordinates. These
0014 %       are transferred to the NetCDF file only and are not used in the
0015 %       interpolation at all.
0016 %       nVerts - number of vertices (nodes) in the unstructured grid.
0017 %       nElems - number of elements in the unstructured grid.
0018 %   vars - a cell array of the variable names to be interpolated on the
0019 %       FVCOM grid in Mobj (e.g. uwnd, U10, vwnd, V10 etc.).
0020 %   data - a struct which contains the following arrays:
0021 %       x - x data (probably best in cartesian for the interpolation)
0022 %       y - y data (probably best in cartesian for the interpolation)
0023 %       The struct must also contain all the variables defined in vars.
0024 %       time - time vector (in Modified Julian Days). If you're using some
0025 %       of the NCEP surface products (e.g. relative humitidy, sea level
0026 %       pressure), you need to supply x and y coordinates for their grids
0027 %       as .xalt and .yalt).
0028 %   varargin - keyword/argument pairs:
0029 %       add_elems - true/false - set to true to enable interpolation of
0030 %       field onto both nodes and elements (defaults to both). This is
0031 %       useful if you run into memory issues as this can effectively halve
0032 %       the memory requirements.
0033 %
0034 % OUTPUT:
0035 %   fvcom - struct of the interpolated data values at the model nodes and
0036 %       element centres (unless add_elems is false, in which case, only
0037 %       nodes). Also includes any variables which were in the input struct
0038 %       but which have not been interpolated (e.g. time).
0039 %
0040 % EXAMPLE USAGE:
0041 %   interpfields = {'uwnd', 'vwnd', 'slp', 'nshf', 'nlwrs', 'nswrs', ...
0042 %       'P_E', 'Et', 'time', 'lon', 'lat', 'x', 'y'};
0043 %   forcing_interp = grid2fvcom(Mobj, interpfields, forcing);
0044 %
0045 % NOTE:
0046 %   The shape of the returned arrays for rhum and slp (via
0047 %   get_NCEP_forcing.m) have sometimes differed from the other vairables
0048 %   (they appear to be projected onto a different grid). Give data.xalt and
0049 %   data.yalt to specify the alternative grid.
0050 %
0051 % Author(s):
0052 %   Pierre Cazenave (Plymouth Marine Laboratory)
0053 %
0054 % Revision history:
0055 %   2012-10-15 First version based on ncep2fvcom_U10V10.m in the
0056 %   fvcom-toolbox.
0057 %   2012-10-16 Removed the code to read the NCEP file. Instead, farmed that
0058 %   out to a new function (read_NCEP_wind) so that the relevant section can
0059 %   be more readily extracted (rather than using the entire globe's data:
0060 %   it's easier to subsample and provide the subsampled data here).
0061 %   2012-10-17 Add outputs to the function for use in visualisation.
0062 %   2012-10-19 Add wind struct as input rather than separate u, v, time and
0063 %   lat/long arrays. Makes invocation a bit cleaner.
0064 %   2012-11-01 Farmed out the creation of the NetCDF file to
0065 %   write_FVCOM_forcing.m and made this purely an interpolation script.
0066 %   2013-02-14 Add support for interpolating data on two different grids
0067 %   through the .xalt and .yalt fields in the input data structure. This is
0068 %   handy if you've got data from NCEP from both the Surface and Gaussian
0069 %   products, each of which are on different grids.
0070 %   2013-05-16 Add parallel for loops if the Parallel Computing Toolbox is
0071 %   available (MATLAB parfor loops fail gracefully to for loops if it is
0072 %   not available, in which case no harm, no foul).
0073 %   2013-07-18 Add more elegant case statement rather than using string
0074 %   comparisons.
0075 %   2013-08-06 Fix fairly significant bug in which the position arrays were
0076 %   transposed relative to the data arrays. The code now checks for the
0077 %   dimensions and warns if they have been flipped to match. There is no
0078 %   checking that the flip has worked because the xalt and yalt arrays
0079 %   complicate things too much for me to figure out today. If you want to
0080 %   implement that functionality, please do so! I also added commented-out
0081 %   figure at the end to check the interpolation has worked properly,
0082 %   should you wish to check manually.
0083 %   2013-12-04 Check for the presence of the input fields being requested
0084 %   in the input struct to avoid finding out that the last field in vars
0085 %   doesn't exist in data. Change the way the alternative coordinate arrays
0086 %   are used to accommodate subtleties in the parallel code in MATLAB.
0087 %   2015-05-20 Update the parallel processing commands.
0088 %   2015-05-22 Add option to disable output on element centres.
0089 %   2016-06-02 Fix the alternative grid handling in the parallel loop.
0090 %   Remove commented out code too.
0091 %
0092 %==========================================================================
0093 
0094 if nargin ~= 3 && isempty(varargin)
0095     error('Incorrect number of arguments')
0096 end
0097 
0098 do_elems = false;
0099 for v = 1:2:length(varargin)
0100     switch varargin{v}
0101         case 'add_elems'
0102             if varargin{v + 1}
0103                 do_elems = true;
0104             end
0105     end
0106 end
0107 
0108 subname = 'grid2fvcom';
0109 
0110 global ftbverbose;
0111 if ftbverbose
0112     fprintf('\nbegin : %s \n', subname)
0113 end
0114 
0115 % Before we go too far into this, check we have all the fields in the input
0116 % data that are being requested.
0117 for ff = 1:length(vars)
0118     assert(isfield(data, vars{ff}), ...
0119         'Missing field %s in the input data struct.', vars{ff})
0120 end
0121 
0122 % Run jobs on multiple workers if we have that functionality. Not sure if
0123 % it's necessary, but check we have the Parallel Toolbox first.
0124 if license('test', 'Distrib_Computing_Toolbox')
0125     % We have the Parallel Computing Toolbox, so launch a bunch of workers.
0126     if isempty(gcp('nocreate'))
0127         % Force pool to be local in case we have remote pools available.
0128         parpool('local');
0129     end
0130 end
0131 
0132 %--------------------------------------------------------------------------
0133 % Get the relevant bits from the FVCOM mesh object
0134 %--------------------------------------------------------------------------
0135 x = Mobj.x;
0136 y = Mobj.y;
0137 nVerts = Mobj.nVerts;
0138 nElems = Mobj.nElems;
0139 if ftbverbose
0140     fprintf('info for FVCOM domain\n');
0141     fprintf('number of nodes: %d\n', nVerts);
0142     fprintf('number of elems: %d\n', nElems);
0143 end
0144 
0145 xc = nodes2elems(x, Mobj);
0146 yc = nodes2elems(y, Mobj);
0147 
0148 try
0149     ntimes = numel(data.time);
0150 catch
0151     ntimes = numel(data.(vars{1}).time);
0152 end
0153 
0154 % Interpolate supplied regularly gridded data to FVCOM mesh. Use
0155 % TriScatteredInterp to do the interpolation instead of griddata (should be
0156 % faster).
0157 for vv = 1:length(vars)
0158     switch vars{vv}
0159         case 'time'
0160             fprintf('transferring variable %s as is\n', vars{vv})
0161             fvcom.(vars{vv}) = data.(vars{vv});
0162             continue
0163 
0164         case {'lat', 'lon', 'x', 'y'}
0165             fprintf('reassigning variable %s from unstructured grid\n', vars{vv})
0166             fvcom.(vars{vv}) = Mobj.(vars{vv});
0167 
0168         case {'xalt', 'yalt'}
0169             % Only exist for the interpolation of some data on an
0170             % alternative grid.
0171             fprintf('skipping %s\n', vars{vv})
0172 
0173         otherwise
0174             % Preallocate the output arrays. Also create temporary arrays
0175             % for the inner loop to be parallelisable (is that a word?):
0176             tmp_fvcom_data = zeros(nElems, ntimes);
0177             tmp_fvcom_node = zeros(nVerts, ntimes);
0178             try
0179                 tmp_data_data = data.(vars{vv}).data; % input to the interpolation
0180             catch msg
0181                 fprintf('Trying for alternative data structure. (%s) ', ...
0182                     msg.message)
0183                 tmp_data_data = data.(vars{vv}); % input to the interpolation
0184                 fprintf('success!\n')
0185             end
0186 
0187             xx = data.x(:);
0188             yy = data.y(:);
0189 
0190             % Check the shapes of the input data match those of the
0191             % position arrays.
0192             [fvx, fvy] = size(data.x);
0193             [ncx, ncy, ~] = size(tmp_data_data);
0194 
0195             if isfield(data, 'xalt')
0196                 [fvxalt, fvyalt] = size(data.xalt);
0197                 if (ncx ~= fvx || ncy ~= fvy) || (ncx ~= fvxalt || ncy ~= fvyalt)
0198                     % Flipping the input array so it hopefully matches the
0199                     % position arrays.
0200                     tmp_data_data = permute(tmp_data_data, [2, 1, 3]);
0201                     warning('Transposed ''%s'' input data to match position array dimensions', vars{vv})
0202                 end
0203             else
0204                 if (ncx ~= fvx || ncy ~= fvy)
0205                     % Flipping the input array so it hopefully matches the
0206                     % position arrays.
0207                     tmp_data_data = permute(tmp_data_data, [2, 1, 3]);
0208                     warning('Transposed ''%s'' input data to match position array dimensions', vars{vv})
0209                 end
0210                 % If we have a land mask, mask off the coastal and land
0211                 % points in the coordinates arrays.
0212                 if isfield(data, 'lsm')
0213                     xx(data.lsm ~= 0) = [];
0214                     yy(data.lsm ~= 0) = [];
0215                 end
0216             end
0217 
0218             % Use a parallel loop for the number of time steps we're
0219             % interpolating.
0220             varname = vars{vv};
0221             parfor i = 1:ntimes
0222                 if ftbverbose
0223                     fprintf('interpolating %s, frame %d of %d\n', varname, i, ntimes);
0224                 end
0225 
0226                 currvar = tmp_data_data(:, :, i);
0227 
0228                 % TriScatteredInterp way (with natural neighbour
0229                 % interpolation). Instead of the quite crude try/catch that
0230                 % was here, count the number of elements in the coordinate
0231                 % (xx and yy) and data (currvar) arrays: if they differ,
0232                 % try the same thing with the xxalt and xyalt coordinate
0233                 % arrays. If they still differ, then error out. The reason
0234                 % for this different approach is that the parfor sometimes
0235                 % failed for me when using non-NCEP data as the source for
0236                 % the interpolation.
0237                 ndata = numel(currvar(~isnan(currvar)));
0238                 nxx = numel(xx);
0239                 nyy = numel(yy);
0240                 % Get the alternate grid, if we have it.
0241                 if isfield(data, 'xalt')
0242                     xxalt = data.xalt(:);
0243                     yyalt = data.yalt(:);
0244                     if isfield(data, 'lsmalt')
0245                         xxalt(data.lsmalt ~= 0) = [];
0246                         yyalt(data.lsmalt ~= 0) = [];
0247                     end
0248                 end
0249                 assert(nxx == nyy, 'Inconsistent coordinate array sizes.')
0250                 if nxx == ndata
0251                     ftsin = TriScatteredInterp(...
0252                         xx, ...
0253                         yy, ...
0254                         currvar(~isnan(currvar(:))), ...
0255                         'natural');
0256                 elseif isfield(data, 'xalt') && numel(xxalt) == ndata
0257                     ftsin = TriScatteredInterp(...
0258                         xxalt, ...
0259                         yyalt, ...
0260                         currvar(~isnan(currvar(:))), ...
0261                         'natural');
0262                 else
0263                     error('Can''t interpolate the data: non-matching coordinate array sizes.')
0264                 end
0265 
0266                 tmp_fvcom_node(:, i) = ftsin(x, y);
0267                 nnans1 = sum(isnan(tmp_fvcom_node(:, i)));
0268                 if  nnans1 > 0
0269                     warning('%i NaNs in the interpolated node data. This won''t work with FVCOM.', nnans1)
0270                 end
0271                 if do_elems
0272                     tmp_fvcom_data(:, i) = ftsin(xc, yc);
0273                     nnans2 = sum(isnan(tmp_fvcom_data(:, i)));
0274                     if nnans2 > 0
0275                         warning('%i NaNs in the interpolated element data. This won''t work with FVCOM.', nnans2)
0276                     end
0277                 end
0278             end
0279             % Transfer the temporary arrays back to the relevant struct and
0280             % clear out the temporary arrays.
0281             fvcom.(vars{vv}).node = tmp_fvcom_node;
0282             if do_elems
0283                 fvcom.(vars{vv}).data = tmp_fvcom_data;
0284             end
0285             clear nnans* tmp_*
0286 
0287             if ftbverbose
0288                 fprintf('interpolation of %s complete\n', vars{vv});
0289             end
0290     end
0291 end
0292 
0293 cleaner = onCleanup(@() delete(gcp('nocreate')));
0294 
0295 if ftbverbose
0296     fprintf('end   : %s \n', subname)
0297 end
0298 
0299 %% Debugging figure to check the interpolation makes sense.
0300 
0301 % close all
0302 %
0303 % figure
0304 %
0305 % vartoplot = 'nswrf';
0306 % tidx = 12; % time index
0307 %
0308 % subplot(2, 1, 1)
0309 % try
0310 %     pcolor(data.lon, data.lat, data.(vartoplot).data(:, :, tidx)')
0311 % catch
0312 %     pcolor(data.lon, data.lat, data.(vartoplot)(:, :, tidx)')
0313 % end
0314 % shading flat
0315 % axis('equal', 'tight')
0316 % title([vartoplot, ' (regularly gridded)'])
0317 % colorbar
0318 % try
0319 %     caxis([min(fvcom.(vartoplot).data(:, tidx)), max(fvcom.(vartoplot).data(:, tidx))])
0320 % catch
0321 %     caxis([min(fvcom.(vartoplot)(:, tidx)), max(fvcom.(vartoplot)(:, tidx))])
0322 % end
0323 % axis([min(Mobj.lon), max(Mobj.lon), min(Mobj.lat), max(Mobj.lat)])
0324 %
0325 % subplot(2, 1, 2)
0326 % try
0327 %     patch('Vertices', [Mobj.lon, Mobj.lat], 'Faces', Mobj.tri, 'cData', fvcom.(vartoplot).data(:, tidx));
0328 % catch
0329 %     patch('Vertices', [Mobj.lon, Mobj.lat], 'Faces', Mobj.tri, 'cData', fvcom.(vartoplot)(:, tidx));
0330 % end
0331 % shading flat
0332 % axis('equal')
0333 % axis([min(data.lon(:)), max(data.lon(:)), min(data.lat(:)), max(data.lat(:))])
0334 % title([vartoplot, ' (interpolated)'])
0335 % colorbar
0336 % try
0337 %     caxis([min(fvcom.(vartoplot).data(:, tidx)), max(fvcom.(vartoplot).data(:, tidx))])
0338 % catch
0339 %     caxis([min(fvcom.(vartoplot)(:, tidx)), max(fvcom.(vartoplot)(:, tidx))])
0340 % end
0341 % axis([min(Mobj.lon), max(Mobj.lon), min(Mobj.lat), max(Mobj.lat)])
0342

Generated on Wed 20-Feb-2019 16:06:01 by m2html © 2005