Home > fvcom_prepro > get_POLCOMS_tsobc.m

get_POLCOMS_tsobc

PURPOSE ^

Read temperature and salinity from POLCOMS NetCDF model output files and

SYNOPSIS ^

function Mobj = get_POLCOMS_tsobc(Mobj, polcoms_ts, polcoms_z)

DESCRIPTION ^

 Read temperature and salinity from POLCOMS NetCDF model output files and
 interpolate onto the open boundaries in Mobj.

 function Mobj = get_POLCOMS_tsobc(Mobj, polcoms_ts, polcoms_bathy, varlist)

 DESCRIPTION:
    Interpolate temperature and salinity values onto the FVCOM open
    boundaries at all sigma levels.

 INPUT:
   Mobj        = MATLAB mesh structure which must contain:
                   - Mobj.siglayz - sigma layer depths for all model
                   nodes.
                   - Mobj.lon, Mobj.lat - node coordinates (lat/long).
                   - Mobj.obc_nodes - list of open boundary node inidices.
                   - Mobj.nObcNodes - number of nodes in each open
                   boundary.
   polcoms_ts  = POLCOMS NetCDF file in which 4D variables of temperature 
                 and salinity (called 'ETW' and 'x1X') exist. Their shape
                 should be (y, x, sigma, time).
   polcoms_z   = POLCOMS NetCDF file in which 4D variables of bathymetry
                 and sigma layer thickness can be found. They should be
                 called 'depth' and 'pdepth' respectively.
 
 OUTPUT:
    Mobj = MATLAB structure in which three new fields (called temperature,
           salinity and ts_time). temperature and salinity have sizes
           (sum(Mobj.nObcNodes), sigma, time). The time dimension is
           determined based on the input NetCDF file. The ts_time variable
           is just the input file times in Modified Julian Day.

 EXAMPLE USAGE
    Mobj = get_POLCOMS_forcing(Mobj, polcoms_ts, polcoms_z)

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

 Revision history
    2013-01-09 First version based on the FVCOM shelf model
    get_POLCOMS_forcing.m script (i.e. not a function but a plain script).

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

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function Mobj = get_POLCOMS_tsobc(Mobj, polcoms_ts, polcoms_z)
0002 % Read temperature and salinity from POLCOMS NetCDF model output files and
0003 % interpolate onto the open boundaries in Mobj.
0004 %
0005 % function Mobj = get_POLCOMS_tsobc(Mobj, polcoms_ts, polcoms_bathy, varlist)
0006 %
0007 % DESCRIPTION:
0008 %    Interpolate temperature and salinity values onto the FVCOM open
0009 %    boundaries at all sigma levels.
0010 %
0011 % INPUT:
0012 %   Mobj        = MATLAB mesh structure which must contain:
0013 %                   - Mobj.siglayz - sigma layer depths for all model
0014 %                   nodes.
0015 %                   - Mobj.lon, Mobj.lat - node coordinates (lat/long).
0016 %                   - Mobj.obc_nodes - list of open boundary node inidices.
0017 %                   - Mobj.nObcNodes - number of nodes in each open
0018 %                   boundary.
0019 %   polcoms_ts  = POLCOMS NetCDF file in which 4D variables of temperature
0020 %                 and salinity (called 'ETW' and 'x1X') exist. Their shape
0021 %                 should be (y, x, sigma, time).
0022 %   polcoms_z   = POLCOMS NetCDF file in which 4D variables of bathymetry
0023 %                 and sigma layer thickness can be found. They should be
0024 %                 called 'depth' and 'pdepth' respectively.
0025 %
0026 % OUTPUT:
0027 %    Mobj = MATLAB structure in which three new fields (called temperature,
0028 %           salinity and ts_time). temperature and salinity have sizes
0029 %           (sum(Mobj.nObcNodes), sigma, time). The time dimension is
0030 %           determined based on the input NetCDF file. The ts_time variable
0031 %           is just the input file times in Modified Julian Day.
0032 %
0033 % EXAMPLE USAGE
0034 %    Mobj = get_POLCOMS_forcing(Mobj, polcoms_ts, polcoms_z)
0035 %
0036 % Author(s):
0037 %    Pierre Cazenave (Plymouth Marine Laboratory)
0038 %
0039 % Revision history
0040 %    2013-01-09 First version based on the FVCOM shelf model
0041 %    get_POLCOMS_forcing.m script (i.e. not a function but a plain script).
0042 %
0043 %==========================================================================
0044 
0045 subname = 'get_POLCOMS_forcing';
0046 
0047 global ftbverbose;
0048 if ftbverbose
0049     fprintf('\n')
0050     fprintf(['begin : ' subname '\n'])
0051 end
0052 
0053 varlist = {'lon', 'lat', 'ETW', 'x1X', 'time'};
0054 
0055 % Get the results
0056 nc = netcdf.open(polcoms_ts, 'NOWRITE');
0057 
0058 for var=1:numel(varlist)
0059     
0060     getVar = varlist{var};
0061     varid_pc = netcdf.inqVarID(nc, getVar);
0062     
0063     data = netcdf.getVar(nc, varid_pc, 'single');
0064     pc.(getVar).data = double(data);
0065     % Try to get some units (important for the calculation of MJD).
0066     try
0067         units = netcdf.getAtt(nc,varid_pc,'units');
0068     catch
0069         units = [];
0070     end
0071     pc.(getVar).units = units;
0072 end
0073 
0074 netcdf.close(nc)
0075 
0076 % Extract the bathymetry ('pdepth' is cell thickness, 'depth' is cell
0077 % centre depth).
0078 nc = netcdf.open(polcoms_z, 'NOWRITE');
0079 varid_pc = netcdf.inqVarID(nc, 'depth'); 
0080 pc.depth.data = double(netcdf.getVar(nc, varid_pc, 'single'));
0081 try
0082     pc.depth.units = netcdf.getAtt(nc, varid_pc, 'units');
0083 catch
0084     pc.depth.units = [];
0085 end
0086 varid_pc = netcdf.inqVarID(nc, 'pdepth'); 
0087 pc.pdepth.data = double(netcdf.getVar(nc, varid_pc, 'single'));
0088 try
0089     pc.pdepth.units = netcdf.getAtt(nc, varid_pc, 'units');
0090 catch
0091     pc.pdepth.units = [];
0092 end
0093 netcdf.close(nc)
0094 
0095 % Data format:
0096 %
0097 %   pc.ETW.data and pc.x1X.data are y, x, sigma, time
0098 %
0099 [~, ~, nz, nt] = size(pc.ETW.data);
0100 
0101 % Make rectangular arrays for the nearest point lookup.
0102 [lon, lat] = meshgrid(pc.lon.data, pc.lat.data);
0103 
0104 % Find the nearest POLCOMS point to each point in the FVCOM open boundaries
0105 fvlon = Mobj.lon(Mobj.obc_nodes(Mobj.obc_nodes ~= 0));
0106 fvlat = Mobj.lat(Mobj.obc_nodes(Mobj.obc_nodes ~= 0));
0107 
0108 % Number of boundary nodes
0109 nf = sum(Mobj.nObcNodes);
0110 % Number of sigma layers.
0111 fz = size(Mobj.siglayz, 2);
0112 
0113 % itemp = nan(nf, nz, nt); % POLCOMS interpolated temperatures
0114 % isal = nan(nf, nz, nt); % POLCOMS interpolated salinities
0115 fvtemp = nan(nf, fz, nt); % FVCOM interpolated temperatures
0116 fvsal = nan(nf, fz, nt); % FVCOM interpolated salinities
0117 
0118 if ftbverbose
0119     tic
0120 end
0121 for t = 1:nt
0122     % Get the current 3D array of POLCOMS results.
0123     pctemp3 = pc.ETW.data(:, :, :, t);
0124     pcsal3 = pc.x1X.data(:, :, :, t);
0125     
0126     % Preallocate the intermediate results arrays.
0127     itempz = nan(nf, nz);
0128     isalz = nan(nf, nz);
0129     idepthz = nan(nf, nz);
0130     
0131     for j = 1:nz
0132         % Now extract the relevant layer from the 3D subsets. Transpose the
0133         % data to be (x, y) rather than (y, x).
0134         pctemp2 = pctemp3(:, :, j)';
0135         pcsal2 = pcsal3(:, :, j)';
0136         pcdepth2 = squeeze(pc.depth.data(:, :, j, t))';
0137        
0138         % Create new arrays which will be flattened when masking (below).
0139         tpctemp2 = pctemp2;
0140         tpcsal2 = pcsal2;
0141         tpcdepth2 = pcdepth2;
0142         tlon = lon;
0143         tlat = lat;
0144         
0145         % Create and apply a mask to remove values outside the domain. This
0146         % inevitably flattens the arrays, but it shouldn't be a problem
0147         % since we'll be searching for the closest values in such a manner
0148         % as is appropriate for an unstructured grid (i.e. we're assuming
0149         % the POLCOMS data is irregularly spaced).
0150         mask = tpcdepth2 < -20000;
0151         tpctemp2(mask) = [];
0152         tpcsal2(mask) = [];
0153         tpcdepth2(mask) = [];
0154         % Also apply the masks to the position arrays so we can't even find
0155         % positions outside the domain, effectively meaning if a value is
0156         % outside the domain, the nearest value to the boundary node will
0157         % be used.
0158         tlon(mask) = [];
0159         tlat(mask) = [];
0160         
0161         % Preallocate the intermediate results arrays.
0162         itempobc = nan(nf, 1);
0163         isalobc = nan(nf, 1);
0164         idepthobc = nan(nf, 1);
0165         
0166         % Speed up the tightest loop with a parallelized loop.
0167         parfor i = 1:nf
0168             % Now we can do each position within the 2D layer.
0169 
0170             fx = fvlon(i);
0171             fy = fvlat(i);
0172 
0173             [~, ii] = sort(sqrt((tlon - fx).^2 + (tlat - fy).^2));
0174             % Get the n nearest nodes from POLCOMS (more? fewer?).
0175             ixy = ii(1:16);
0176 
0177             % Get the variables into static variables for the
0178             % parallelisation.
0179             plon = tlon(ixy);
0180             plat = tlat(ixy);
0181             ptemp = tpctemp2(ixy);
0182             psal = tpcsal2(ixy);
0183             pdepth = tpcdepth2(ixy);
0184             
0185             % Use a triangulation to do the horizontal interpolation.
0186             tritemp = TriScatteredInterp(plon', plat', ptemp', 'natural');
0187             trisal = TriScatteredInterp(plon', plat', psal', 'natural');
0188             triz = TriScatteredInterp(plon', plat', pdepth', 'natural');
0189             itempobc(i) = tritemp(fx, fy);
0190             isalobc(i) = trisal(fx, fy);
0191             idepthobc(i) = triz(fx, fy);
0192             
0193             % Check all three, though if one is NaN, they all will be.
0194             if isnan(itempobc(i)) || isnan(isalobc(i)) || isnan(idepthobc(i))
0195                 warning('FVCOM boundary node at %f, %f is outside the POLCOMS domain. Setting to the closest POLCOMS value.', fx, fy)
0196                 itempobc(i) = tpctemp2(ii(1));
0197                 isalobc(i) = tpcsal2(ii(1));
0198                 idepthobc(i) = tpcdepth2(ii(1));
0199             end
0200         end
0201         
0202         % Put the results in this intermediate array.
0203         itempz(:, j) = itempobc;
0204         isalz(:, j) = isalobc;
0205         idepthz(:, j) = idepthobc;
0206     end
0207 
0208     % Now we've interpolated in space, we can interpolate the z-values
0209     % to the sigma depths.
0210     oNodes = Mobj.obc_nodes(Mobj.obc_nodes ~= 0);
0211     for zi = 1:fz
0212 
0213         % Preallocate the output arrays
0214         fvtempz = nan(nf, fz);
0215         fvsalz = nan(nf, fz);
0216 
0217         for pp = 1:nf
0218             % Get the FVCOM depths at this node
0219             tfz = Mobj.siglayz(oNodes(pp), :);
0220             % Now get the interpolated POLCOMS depth at this node
0221             tpz = idepthz(pp, :);
0222 
0223             % Get the temperature and salinity values for this node and
0224             % interpolate down the water column (from POLCOMS to FVCOM).
0225             % TODO: Use csaps for the vertical interplation/subsampling at
0226             % each location. Alternatively, the pchip interp1 method seems
0227             % to do a decent job of the interpolation; it might be a more
0228             % suitable candidate in the absence of csaps. In fact, the demo
0229             % of csaps in the MATLAB documentation makes the interpolation
0230             % look horrible (shaving off extremes). I think pchip is
0231             % better.
0232             if ~isnan(tpz)
0233                 fvtempz(pp, :) = interp1(tpz, itempz(pp, :), tfz, 'linear', 'extrap');
0234                 fvsalz(pp, :) = interp1(tpz, isalz(pp, :), tfz, 'linear', 'extrap');
0235             else
0236                 warning('Should never see this... ') % because we test for NaNs when fetching the values.
0237                 warning('FVCOM boundary node at %f, %f is outside the POLCOMS domain. Skipping.', fvlon(pp), fvlat(pp))
0238                 continue
0239             end
0240         end
0241     end
0242     
0243     % The horizontally-interpolated values in the final results array.
0244 %     itemp(:, :, t) = itempz;
0245 %     isal(:, :, t) = isalz;
0246     % The horizontally- and vertically-interpolated values in the final
0247     % FVCOM results array.
0248     fvtemp(:, :, t) = fvtempz;
0249     fvsal(:, :, t) = fvsalz;
0250 end
0251 if ftbverbose
0252     toc
0253 end
0254 
0255 Mobj.temperature = fvtemp;
0256 Mobj.salt = fvsal;
0257 
0258 % Do we have to interpolate to the FVCOM time series? Looking at page 325
0259 % of the FVCOM manual, it looks like the temperature and salinity are on a
0260 % different time sampling from the other example files (14 time steps vs.
0261 % 3625 for _wnd.nc or 43922 for _julain_obc.nc (i.e. surface elevation at
0262 % the boundary)). That's not to say those files were all used in the same
0263 % model run... In the interim, just convert the current times to Modified
0264 % Julian Day (this is a bit ugly).
0265 % pc.time.yyyymmdd = strtrim(regexp(pc.time.units, 'since', 'split'));
0266 % pc.time.yyyymmdd = str2double(regexp(pc.time.yyyymmdd{end}, '-', 'split'));
0267 % Mobj.ts_times = greg2mjulian(pc.time.yyyymmdd(1), pc.time.yyyymmdd(2), pc.time.yyyymmdd(3), 0, 0, 0) + (pc.time.data / 3600 / 24);
0268 
0269 % Convert the POLCOMS times to Modified Julian Day (this is a very ugly).
0270 pc.time.yyyymmdd = strtrim(regexp(pc.time.units, 'since', 'split'));
0271 pc.time.strtime = regexp(pc.time.yyyymmdd{end}, '-', 'split');
0272 % This new version of the time has the year in a weird format (yr.#). We
0273 % thus need to split it again to get the decimal year (post-2000 only?).
0274 pc.time.strtimeyr = regexp(pc.time.strtime, '\.', 'split');
0275 pc.time.yyyymmdd = str2double([pc.time.strtimeyr{1}(2), pc.time.strtime{2:3}]);
0276 pc.time.yyyymmdd(1) = pc.time.yyyymmdd(1) + 2000; % add full year.
0277 Mobj.ts_times = greg2mjulian(pc.time.yyyymmdd(1), pc.time.yyyymmdd(2), pc.time.yyyymmdd(3), 0, 0, 0) + (pc.time.data / 3600 / 24);
0278 
0279 if ftbverbose
0280     fprintf(['end   : ' subname '\n'])
0281 end

Generated on Mon 04-Feb-2013 14:22:28 by m2html © 2005