


Interpolate FVCOM onto regular grid
grid.
fvcom2grid(Mobj,vars,data)
DESCRIPTION:
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).
OUTPUT:
EXAMPLE USAGE:
interpfields = {'uwnd', 'vwnd', 'slp', 'nshf', 'nlwrs', 'nswrs', ...
'P_E', 'Et', 'time', 'lon', 'lat', 'x', 'y'};
forcing_interp = grid2fvcom(Mobj, interpfields, forcing);
NOTE:
Author(s):
Pierre Cazenave (Plymouth Marine Laboratory)
Revision history:
2014-03-21 First version based on grid2fvcom.m in the
fvcom-toolbox.
==========================================================================

0001 function fvcom = fvcom2grid(Mobj, vars, data) 0002 % Interpolate FVCOM onto regular grid 0003 % grid. 0004 % 0005 % fvcom2grid(Mobj,vars,data) 0006 % 0007 % DESCRIPTION: 0008 % 0009 % INPUT: 0010 % Mobj - MATLAB mesh object with the following fields: 0011 % x, y, lon, lat - cartesian and spherical node coordinates. These 0012 % are transferred to the NetCDF file only and are not used in the 0013 % interpolation at all. 0014 % nVerts - number of vertices (nodes) in the unstructured grid. 0015 % nElems - number of elements in the unstructured grid. 0016 % vars - a cell array of the variable names to be interpolated on the 0017 % FVCOM grid in Mobj (e.g. uwnd, U10, vwnd, V10 etc.). 0018 % data - a struct which contains the following arrays: 0019 % x - x data (probably best in cartesian for the interpolation) 0020 % y - y data (probably best in cartesian for the interpolation) 0021 % The struct must also contain all the variables defined in vars. 0022 % time - time vector (in Modified Julian Days). If you're using some 0023 % of the NCEP surface products (e.g. relative humitidy, sea level 0024 % pressure), you need to supply x and y coordinates for their grids 0025 % as .xalt and .yalt). 0026 % 0027 % OUTPUT: 0028 % 0029 % EXAMPLE USAGE: 0030 % interpfields = {'uwnd', 'vwnd', 'slp', 'nshf', 'nlwrs', 'nswrs', ... 0031 % 'P_E', 'Et', 'time', 'lon', 'lat', 'x', 'y'}; 0032 % forcing_interp = grid2fvcom(Mobj, interpfields, forcing); 0033 % 0034 % NOTE: 0035 % 0036 % Author(s): 0037 % Pierre Cazenave (Plymouth Marine Laboratory) 0038 % 0039 % Revision history: 0040 % 2014-03-21 First version based on grid2fvcom.m in the 0041 % fvcom-toolbox. 0042 % 0043 %========================================================================== 0044 0045 if nargin ~= 3 0046 error('Incorrect number of arguments') 0047 end 0048 0049 subname = 'grid2fvcom'; 0050 0051 global ftbverbose; 0052 if ftbverbose 0053 fprintf('\nbegin : %s \n', subname) 0054 end 0055 0056 % Before we go too far into this, check we have all the fields in the input 0057 % data that are being requested. 0058 for ff = 1:length(vars) 0059 assert(isfield(data, vars{ff}), ... 0060 'Missing field %s in the input data struct.', vars{ff}) 0061 end 0062 0063 % Run jobs on multiple workers if we have that functionality. Not sure if 0064 % it's necessary, but check we have the Parallel Toolbox first. 0065 % wasOpened = false; 0066 if license('test', 'Distrib_Computing_Toolbox') 0067 % We have the Parallel Computing Toolbox, so launch a bunch of workers. 0068 if matlabpool('size') == 0 0069 % Force pool to be local in case we have remote pools available. 0070 matlabpool open local 0071 % wasOpened = true; 0072 end 0073 end 0074 0075 %-------------------------------------------------------------------------- 0076 % Get the relevant bits from the FVCOM mesh object 0077 %-------------------------------------------------------------------------- 0078 x = Mobj.x; 0079 y = Mobj.y; 0080 nVerts = Mobj.nVerts; 0081 nElems = Mobj.nElems; 0082 if ftbverbose 0083 fprintf('info for FVCOM domain\n'); 0084 fprintf('number of nodes: %d\n', nVerts); 0085 fprintf('number of elems: %d\n', nElems); 0086 end 0087 0088 xc = nodes2elems(x, Mobj); 0089 yc = nodes2elems(y, Mobj); 0090 0091 try 0092 ntimes = numel(data.time); 0093 catch 0094 ntimes = numel(data.(vars{1}).time); 0095 end 0096 0097 % Interpolate supplied regularly gridded data to FVCOM mesh. Use 0098 % TriScatteredInterp to do the interpolation instead of griddata (should be 0099 % faster). 0100 for vv = 1:length(vars) 0101 switch vars{vv} 0102 case 'time' 0103 fprintf('transferring variable %s as is\n', vars{vv}) 0104 fvcom.(vars{vv}) = data.(vars{vv}); 0105 continue 0106 0107 case {'lat', 'lon', 'x', 'y'} 0108 fprintf('reassigning variable %s from unstructured grid\n', vars{vv}) 0109 fvcom.(vars{vv}) = Mobj.(vars{vv}); 0110 0111 case {'xalt', 'yalt'} 0112 % Only exist for the interpolation of some data on an 0113 % alternative grid. 0114 fprintf('skipping %s\n', vars{vv}) 0115 0116 otherwise 0117 % Preallocate the output arrays. 0118 % Serial version: 0119 % fvcom.(vars{vv}).data = zeros(nElems, ntimes); 0120 % fvcom.(vars{vv}).node = zeros(nVerts, ntimes); 0121 % Also create temporary arrays for the inner loop to be 0122 % parallelisable (is that a word?): 0123 tmp_fvcom_data = zeros(nElems, ntimes); 0124 tmp_fvcom_node = zeros(nVerts, ntimes); 0125 try 0126 tmp_data_data = data.(vars{vv}).data; % input to the interpolation 0127 catch msg 0128 fprintf('Trying for alternative data structure. (%s) ', ... 0129 msg.message) 0130 tmp_data_data = data.(vars{vv}); % input to the interpolation 0131 fprintf('success!\n') 0132 end 0133 0134 xx = data.x(:); 0135 yy = data.y(:); 0136 % Sometimes the parfor loop will fail if xxalt and yyalt 0137 % aren't defined at all. So, make them empty here. This 0138 % shouldn't impact data where we need those alternative arrays 0139 % because if the data.xalt and data.yalt arrays exist, then 0140 % these values will be overwritten with them. It does ensure 0141 % that xxalt and yyalt always exist though. 0142 xxalt = []; 0143 yyalt = []; 0144 0145 % Check the shapes of the input data match those of the 0146 % position arrays. 0147 [fvx, fvy] = size(data.x); 0148 [ncx, ncy, ~] = size(tmp_data_data); 0149 0150 if isfield(data, 'xalt') 0151 [fvxalt, fvyalt] = size(data.xalt); 0152 xxalt = data.xalt(:); 0153 yyalt = data.yalt(:); 0154 if (ncx ~= fvx || ncy ~= fvy) || (ncx ~= fvxalt || ncy ~= fvyalt) 0155 % Flipping the input array so it hopefully matches the 0156 % position arrays. 0157 tmp_data_data = permute(tmp_data_data, [2, 1, 3]); 0158 warning('Transposed ''%s'' input data to match position array dimensions', vars{vv}) 0159 end 0160 if isfield(data, 'lsmalt') 0161 % If we have a land mask, mask off the coastal and land 0162 % points in the coordinates arrays with the alternative 0163 % mask. 0164 xxalt(data.lsmalt ~= 0) = []; 0165 yyalt(data.lsmalt ~= 0) = []; 0166 end 0167 else 0168 if (ncx ~= fvx || ncy ~= fvy) 0169 % Flipping the input array so it hopefully matches the 0170 % position arrays. 0171 tmp_data_data = permute(tmp_data_data, [2, 1, 3]); 0172 warning('Transposed ''%s'' input data to match position array dimensions', vars{vv}) 0173 end 0174 % If we have a land mask, mask off the coastal and land points 0175 % in the coordinates arrays. 0176 if isfield(data, 'lsm') 0177 xx(data.lsm ~= 0) = []; 0178 yy(data.lsm ~= 0) = []; 0179 end 0180 end 0181 0182 % Use a parallel loop for the number of time steps we're 0183 % interpolating. 0184 varname = vars{vv}; 0185 parfor i = 1:ntimes 0186 if ftbverbose 0187 fprintf('interpolating %s, frame %d of %d\n', varname, i, ntimes); 0188 end 0189 0190 % Serial version: 0191 % currvar = data.(vars{vv}).data(:, :, i); 0192 % Parallel version: 0193 currvar = tmp_data_data(:, :, i); 0194 0195 % griddata way (cubic interpolation) 0196 %fvcom.(vars{vv}).node(:,i) = griddata(wind.x,wind.y,currvar,x,y,'cubic'); 0197 %fvcom.(vars{vv}).data(:,i) = griddata(wind.x,wind.y,currvar,xc,yc,'cubic'); 0198 0199 % TriScatteredInterp way (with natural neighbour 0200 % interpolation). Instead of the quite crude try/catch that 0201 % was here, count the number of elements in the coordinate 0202 % (xx and yy) and data (currvar) arrays: if they differ, 0203 % try the same thing with the xxalt and xyalt coordinate 0204 % arrays. If they still differ, then error out. The reason 0205 % for this different approach is that the parfor sometimes 0206 % failed for me when using non-NCEP data as the source for 0207 % the interpolation. 0208 ndata = numel(currvar(~isnan(currvar))); 0209 nxx = numel(xx); 0210 nyy = numel(yy); 0211 assert(nxx == nyy, 'Inconsistent coordinate array sizes.') 0212 if nxx == ndata 0213 ftsin = TriScatteredInterp(... 0214 xx, ... 0215 yy, ... 0216 currvar(~isnan(currvar(:))), ... 0217 'natural'); 0218 elseif exist('xxalt', 'var') && numel(xxalt) == ndata 0219 ftsin = TriScatteredInterp(... 0220 xxalt, ... 0221 yyalt, ... 0222 currvar(~isnan(currvar(:))), ... 0223 'natural'); 0224 else 0225 error('Can''t interpolate the data: non-matching coordinate array sizes.') 0226 end 0227 % try 0228 % ftsin = TriScatteredInterp(... 0229 % xx, ... 0230 % yy, ... 0231 % currvar(~isnan(currvar(:))), ... 0232 % 'natural'); 0233 % catch err 0234 % % In my experience, the matlabpool size - 1 is the 0235 % % first iteration that actually gets printed to the 0236 % % display. 0237 % if i == matlabpool('size') - 1 0238 % % Only print the warning on the "first" iteration. 0239 % warning([err.identifier, ': Some NCEP data are projected' ... 0240 % ' onto a different grid. Check you have specified' ... 0241 % ' data.xalt and data.yalt arrays which are on the' ... 0242 % ' same grid as the data to be interpolated.']) 0243 % end 0244 % ftsin = TriScatteredInterp(xxalt, yyalt, ... 0245 % currvar(~isnan(currvar(:))), 'natural'); 0246 % end 0247 0248 % Serial version: 0249 % fvcom.(vars{vv}).node(:,i) = ftsin(x,y); 0250 % fvcom.(vars{vv}).data(:,i) = ftsin(xc,yc); 0251 % nnans(1) = sum(isnan(fvcom.(vars{vv}).node(:,i))); 0252 % nnans(2) = sum(isnan(fvcom.(vars{vv}).data(:,i))); 0253 % Parallel version: 0254 tmp_fvcom_node(:, i) = ftsin(x, y); 0255 tmp_fvcom_data(:, i) = ftsin(xc, yc); 0256 nnans1 = sum(isnan(tmp_fvcom_node(:, i))); 0257 nnans2 = sum(isnan(tmp_fvcom_data(:, i))); 0258 if nnans1 > 0 0259 warning('%i NaNs in the interpolated node data. This won''t work with FVCOM.', nnans1) 0260 end 0261 if nnans2 > 0 0262 warning('%i NaNs in the interpolated element data. This won''t work with FVCOM.', nnans2) 0263 end 0264 end 0265 % Transfer the temporary arrays back to the relevant struct and 0266 % clear out the temporary arrays. 0267 fvcom.(vars{vv}).node = tmp_fvcom_node; 0268 fvcom.(vars{vv}).data = tmp_fvcom_data; 0269 clear nnans* tmp_* 0270 0271 if ftbverbose 0272 fprintf('interpolation of %s complete\n', vars{vv}); 0273 end 0274 end 0275 end 0276 0277 % if wasOpened 0278 % matlabpool close 0279 % end 0280 0281 % Better way of closing the pool after each invocation (though this might 0282 % incur some overhead due to the time it takes to spin up/close down a 0283 % MATLAB pool of workers). 0284 cleaner = onCleanup(@() matlabpool('close')); 0285 0286 if ftbverbose 0287 fprintf('end : %s \n', subname) 0288 end 0289 0290 %% Debugging figure to check the interpolation makes sense. 0291 0292 % close all 0293 % 0294 % figure 0295 % 0296 % vartoplot = 'nswrf'; 0297 % tidx = 12; % time index 0298 % 0299 % subplot(2, 1, 1) 0300 % try 0301 % pcolor(data.lon, data.lat, data.(vartoplot).data(:, :, tidx)') 0302 % catch 0303 % pcolor(data.lon, data.lat, data.(vartoplot)(:, :, tidx)') 0304 % end 0305 % shading flat 0306 % axis('equal', 'tight') 0307 % title([vartoplot, ' (regularly gridded)']) 0308 % colorbar 0309 % try 0310 % caxis([min(fvcom.(vartoplot).data(:, tidx)), max(fvcom.(vartoplot).data(:, tidx))]) 0311 % catch 0312 % caxis([min(fvcom.(vartoplot)(:, tidx)), max(fvcom.(vartoplot)(:, tidx))]) 0313 % end 0314 % axis([min(Mobj.lon), max(Mobj.lon), min(Mobj.lat), max(Mobj.lat)]) 0315 % 0316 % subplot(2, 1, 2) 0317 % try 0318 % patch('Vertices', [Mobj.lon, Mobj.lat], 'Faces', Mobj.tri, 'cData', fvcom.(vartoplot).data(:, tidx)); 0319 % catch 0320 % patch('Vertices', [Mobj.lon, Mobj.lat], 'Faces', Mobj.tri, 'cData', fvcom.(vartoplot)(:, tidx)); 0321 % end 0322 % shading flat 0323 % axis('equal') 0324 % axis([min(data.lon(:)), max(data.lon(:)), min(data.lat(:)), max(data.lat(:))]) 0325 % title([vartoplot, ' (interpolated)']) 0326 % colorbar 0327 % try 0328 % caxis([min(fvcom.(vartoplot).data(:, tidx)), max(fvcom.(vartoplot).data(:, tidx))]) 0329 % catch 0330 % caxis([min(fvcom.(vartoplot)(:, tidx)), max(fvcom.(vartoplot)(:, tidx))]) 0331 % end 0332 % axis([min(Mobj.lon), max(Mobj.lon), min(Mobj.lat), max(Mobj.lat)]) 0333