Home > fvcom_prepro > get_EHYPE_rivers.m

get_EHYPE_rivers

PURPOSE ^

Extract river discharges from the supplied river positions for the FVCOM

SYNOPSIS ^

function Mobj = get_EHYPE_rivers(Mobj, dist_thresh, varargin)

DESCRIPTION ^

 Extract river discharges from the supplied river positions for the FVCOM
 grid in Mobj.

 get_EHYPE_rivers(Mobj, dist_thresh)

 DESCRIPTION:
   For the positions in Mobj.rivers.positions, find the nearest
   unstructured grid node and extract the river discharge from
   Mobj.rivers.river_flux. The river positions must fall within the
   specified distance (dist_thresh). If multiple rivers are assigned to
   the same node, the river with the larger of the set of discharges is
   used.

 INPUT:
   Mobj - MATLAB mesh object containing:
       * have_lonlat - boolean to check for spherical coordinates.
       * lon, lat - positions for the unstructured grid.
       * tri - triangulation table for the unstructured grid.
       * nVerts - number of nodes in the grid.
       * read_obc_nodes - open boundary node IDs.
       * rivers - river data struct with the following fields:
           - positions - river positions in lon, lat.
           - names - list of river names
           - river_flux - path to the EHYPE ASCII file data directory.
   dist_thresh - maximum distance away from a river node beyond
       which the search for an FVCOM node is abandoned. Units in degrees.
   model_year - [optional] when giving climatology, a year must be
       specified so that the time series can be anchored in time. The
       returned time series will be 3 years long centred on the specified
       year. Discharges will be repeated for the two additional years.
   exclude - [optional] give an array of river numbers to exclude (for
       example if they are particularly bad quality data or are within the
       threshold but otherwise should be excluded from the model). Note,
       these must be numbers (not strings).
   ceh - [optional] if set as true, then the format of the discharge data
       is assumed to be "time,flux" which is valid if using non-EHYPE
       derived flux data (e.g. CEH-derived climatology).

 OUTPUT:
   Mobj.river_flux - volume flux at the nodes within the model domain.
   Mobj.river_nodes - node IDs for the rivers. At the moment, these are
       point sources only. Eventually, some rivers may have to be split
       over several nodes.
   Mobj.river_names - river names which fall within the model domain. For
       rivers where the discharge has been summed, the name is compoud,
       with each contributing name separated by a hyphen (-).
   Mobj.river_time - time series for the river discharge data

 EXAMPLE USAGE:
   Mobj = get_EHYPE_rivers(Mobj, 0.15)

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

 Revision history:
   2013-10-15 - First version based on get_FVCOM_rivers.m.
   2013-11-14 - Update the help to reflect the functionality.
   2013-11-15 - Add support for using a river climatology from the E-HYPE
   time series data (must be precomputed) instead of a specified section
   of the E-HYPE model output.
   2013-12-12 - Remove some redundant variables.
   2013-12-13 - Remove the loop through the time at the end and instead
   use greg2mjulian to work on the whole time vector array.
   2014-01-22 - For nodes with mulitple rivers assigned, use the largest
   of the rivers rather than summing their fluxes. Also eliminate having
   two adjacent river nodes (instead use the average of their flux and
   assign the position to the first river node).
   2014-05-15 - Add option to exclude rivers by name.
   2014-05-19 - Add new option to use an alternatively formatted input
   climatology (two columns instead of the number in the EHYPE data).
   2014-05-20 - Set boolean flag to true to indicate rivers and add number
   of rivers to the relevant field.
   2014-05-29 - Fix issues with the climatology vs. timeseries allocation
   of the output arrays.
   2015-09-24 - Add check for whether we actually have any rivers to
   process.
   2016-01-15 - Fix exclude behaviour for more than a single river to
   exclude. Also summarised the number of included/skipped/ignored rivers
   instead of printing a new line for each river that's skipped.

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

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function Mobj = get_EHYPE_rivers(Mobj, dist_thresh, varargin)
0002 % Extract river discharges from the supplied river positions for the FVCOM
0003 % grid in Mobj.
0004 %
0005 % get_EHYPE_rivers(Mobj, dist_thresh)
0006 %
0007 % DESCRIPTION:
0008 %   For the positions in Mobj.rivers.positions, find the nearest
0009 %   unstructured grid node and extract the river discharge from
0010 %   Mobj.rivers.river_flux. The river positions must fall within the
0011 %   specified distance (dist_thresh). If multiple rivers are assigned to
0012 %   the same node, the river with the larger of the set of discharges is
0013 %   used.
0014 %
0015 % INPUT:
0016 %   Mobj - MATLAB mesh object containing:
0017 %       * have_lonlat - boolean to check for spherical coordinates.
0018 %       * lon, lat - positions for the unstructured grid.
0019 %       * tri - triangulation table for the unstructured grid.
0020 %       * nVerts - number of nodes in the grid.
0021 %       * read_obc_nodes - open boundary node IDs.
0022 %       * rivers - river data struct with the following fields:
0023 %           - positions - river positions in lon, lat.
0024 %           - names - list of river names
0025 %           - river_flux - path to the EHYPE ASCII file data directory.
0026 %   dist_thresh - maximum distance away from a river node beyond
0027 %       which the search for an FVCOM node is abandoned. Units in degrees.
0028 %   model_year - [optional] when giving climatology, a year must be
0029 %       specified so that the time series can be anchored in time. The
0030 %       returned time series will be 3 years long centred on the specified
0031 %       year. Discharges will be repeated for the two additional years.
0032 %   exclude - [optional] give an array of river numbers to exclude (for
0033 %       example if they are particularly bad quality data or are within the
0034 %       threshold but otherwise should be excluded from the model). Note,
0035 %       these must be numbers (not strings).
0036 %   ceh - [optional] if set as true, then the format of the discharge data
0037 %       is assumed to be "time,flux" which is valid if using non-EHYPE
0038 %       derived flux data (e.g. CEH-derived climatology).
0039 %
0040 % OUTPUT:
0041 %   Mobj.river_flux - volume flux at the nodes within the model domain.
0042 %   Mobj.river_nodes - node IDs for the rivers. At the moment, these are
0043 %       point sources only. Eventually, some rivers may have to be split
0044 %       over several nodes.
0045 %   Mobj.river_names - river names which fall within the model domain. For
0046 %       rivers where the discharge has been summed, the name is compoud,
0047 %       with each contributing name separated by a hyphen (-).
0048 %   Mobj.river_time - time series for the river discharge data
0049 %
0050 % EXAMPLE USAGE:
0051 %   Mobj = get_EHYPE_rivers(Mobj, 0.15)
0052 %
0053 % Author(s):
0054 %   Pierre Cazenave (Plymouth Marine Laboratory)
0055 %
0056 % Revision history:
0057 %   2013-10-15 - First version based on get_FVCOM_rivers.m.
0058 %   2013-11-14 - Update the help to reflect the functionality.
0059 %   2013-11-15 - Add support for using a river climatology from the E-HYPE
0060 %   time series data (must be precomputed) instead of a specified section
0061 %   of the E-HYPE model output.
0062 %   2013-12-12 - Remove some redundant variables.
0063 %   2013-12-13 - Remove the loop through the time at the end and instead
0064 %   use greg2mjulian to work on the whole time vector array.
0065 %   2014-01-22 - For nodes with mulitple rivers assigned, use the largest
0066 %   of the rivers rather than summing their fluxes. Also eliminate having
0067 %   two adjacent river nodes (instead use the average of their flux and
0068 %   assign the position to the first river node).
0069 %   2014-05-15 - Add option to exclude rivers by name.
0070 %   2014-05-19 - Add new option to use an alternatively formatted input
0071 %   climatology (two columns instead of the number in the EHYPE data).
0072 %   2014-05-20 - Set boolean flag to true to indicate rivers and add number
0073 %   of rivers to the relevant field.
0074 %   2014-05-29 - Fix issues with the climatology vs. timeseries allocation
0075 %   of the output arrays.
0076 %   2015-09-24 - Add check for whether we actually have any rivers to
0077 %   process.
0078 %   2016-01-15 - Fix exclude behaviour for more than a single river to
0079 %   exclude. Also summarised the number of included/skipped/ignored rivers
0080 %   instead of printing a new line for each river that's skipped.
0081 %
0082 %==========================================================================
0083 
0084 subname = 'get_EHYPE_rivers';
0085 
0086 global ftbverbose;
0087 if ftbverbose
0088     fprintf(['\nbegin : ' subname '\n'])
0089 end
0090 
0091 % Check inputs
0092 if ~Mobj.have_lonlat
0093     error('Require unstructured grid positions in lon/lat format to compare against supplied river positions.')
0094 end
0095 
0096 % Default to standard EHYPE formatted data and no ignored rivers.
0097 yr = [];
0098 ignore_list = [];
0099 ceh = false;
0100 
0101 % If we have only three arguments, we have to assume we've been given a
0102 % year for the climatology. Otherwise, we need to read the arguments based
0103 % on keyword-value pairs. Really, we want keyword-value pairs all the time,
0104 % so silently work when given three arguments and don't mention it in the
0105 % help. This is going to bite me at some point in the future, I'm sure.
0106 if nargin == 3
0107     yr = varargin{1};
0108 elseif nargin > 3
0109     for aa = 1:2:length(varargin)
0110         switch varargin{aa}
0111             case 'model_year'
0112                 yr = varargin{aa + 1};
0113             case 'exclude'
0114                 ignore_list = varargin{aa + 1};
0115             case 'ceh'
0116                 ceh = varargin{aa + 1};
0117         end
0118     end
0119 end
0120 
0121 if (isempty(yr) && ceh) || (~isempty(yr) && ~isnumeric(yr))
0122     error('Trying to do climatology, but don''t have an anchor year. Supply one via the ''model_year'' keyword-value pair.')
0123 end
0124 
0125 % Separate the inputs into separate arrays.
0126 ehype_name = Mobj.rivers.names;
0127 ehype_xy = Mobj.rivers.positions;
0128 ehype_flow = Mobj.rivers.river_flux;
0129 
0130 % If we've been given rivers to ignore, remove them now.
0131 if ~isempty(ignore_list)
0132     ignore_mask = true(length(ehype_name), 1);
0133     for ig = 1:length(ignore_list)
0134         ignore_mask = ignore_mask .* (ehype_name ~= ignore_list(ig));
0135     end
0136     ignore_mask = logical(ignore_mask);
0137 
0138     ehype_name = ehype_name(ignore_mask);
0139     ehype_xy = ehype_xy(ignore_mask, :);
0140 end
0141 
0142 fv_nr = length(ehype_name);
0143 
0144 % Check each location in the EHYPE positions against the grid in Mobj and
0145 % for the indices within the dist_thresh, load and extract the relevant
0146 % time series data.
0147 
0148 vc = 0; % valid FVCOM boundary node counter
0149 
0150 % We need to find the unstructured grid boundary nodes and exclude the open
0151 % boundary nodes from them. This will be our list of potential candidates
0152 % for the river nodes (i.e. the land coastline).
0153 [~, ~, ~, bnd] = connectivity([Mobj.lon, Mobj.lat], Mobj.tri);
0154 boundary_nodes = 1:Mobj.nVerts;
0155 boundary_nodes = boundary_nodes(bnd);
0156 coast_nodes = boundary_nodes(~ismember(boundary_nodes, [Mobj.read_obc_nodes{:}]));
0157 tlon = Mobj.lon(coast_nodes);
0158 tlat = Mobj.lat(coast_nodes);
0159 
0160 fv_obc = nan;
0161 fvcom_names = cell(0);
0162 
0163 % Initialise the flow array with a 366 day long time series of nans. This
0164 % array will be appended to (unless all rivers are outside the domain).
0165 % Only do this if we're doing climatology (signified by a non-empty year).
0166 skipped = 0;
0167 if ~isempty(yr)
0168     fv_flow = nan(366, 1);
0169 end
0170 for ff = 1:fv_nr
0171     % Find the coastline node closest to this river.
0172     fv_dist = sqrt( ...
0173         (ehype_xy(ff, 1) - tlon).^2 + ...
0174         (ehype_xy(ff, 2) - tlat).^2);
0175     [c, idx] = min(fv_dist);
0176     if c > dist_thresh
0177         skipped = skipped + 1;
0178         continue
0179     else
0180         if ftbverbose
0181             fprintf('candidate river %07d found (%f, %f)... ', ehype_name(ff), ehype_xy(ff, 1), ehype_xy(ff, 2))
0182         end
0183     end
0184 
0185     vc = vc + 1;
0186 
0187     % We need to make sure the element in which this node occurs does not
0188     % have two land boundaries (otherwise the model sometimes just fills up
0189     % that element without releasing the water into the adjacent element).
0190 
0191     % Find the other nodes which are joined to the node we've just found.
0192     % We don't need the column to get the other nodes in the element, only
0193     % the row is required.
0194     [row, ~] = find(Mobj.tri == coast_nodes(idx));
0195 
0196     if length(row) == 1
0197         % This is a bad node because it is a part of only one element. The
0198         % rivers need two adjacent elements to work reliably (?). So, we
0199         % need to repeat the process above until we find a node that's
0200         % connected to two elements. We'll try the other nodes in the
0201         % current element before searching the rest of the coastline (which
0202         % is computationally expensive).
0203 
0204         % Remove the current node index from the list of candidates (i.e.
0205         % leave only the two other nodes in the element).
0206         mask = Mobj.tri(row, :) ~= coast_nodes(idx);
0207         n_tri = Mobj.tri(row, mask);
0208 
0209         % Remove values which aren't coastline values (we don't want to set
0210         % the river node to an open water node).
0211         n_tri = intersect(n_tri, coast_nodes);
0212 
0213         % Of the remaining nodes in the element, find the closest one to
0214         % the original river location (in fvcom_xy).
0215         [~, n_idx] = sort(sqrt( ...
0216             (ehype_xy(ff, 1) - Mobj.lon(n_tri)).^2 ...
0217             + (ehype_xy(ff, 2) - Mobj.lon(n_tri)).^2));
0218 
0219         [row_2, ~] = find(Mobj.tri == n_tri(n_idx(1)));
0220         if length(n_idx) > 1
0221             [row_3, ~] = find(Mobj.tri == n_tri(n_idx(2)));
0222         end
0223         % Closest first
0224         if length(row_2) > 1
0225             idx = find(coast_nodes == n_tri(n_idx(1)));
0226         % The other one (only if we have more than one node to consider).
0227         elseif length(n_idx) > 1 && length(row_3) > 1
0228             idx = find(coast_nodes == n_tri(n_idx(2)));
0229         % OK, we need to search across all the other coastline nodes.
0230         else
0231             % TODO: Implement a search of all the other coastline nodes.
0232             % My testing indicates that we never get here (at least for the
0233             % grids I've tested). I'd be interested to see the mesh which
0234             % does get here...
0235             continue
0236         end
0237         if ftbverbose
0238             fprintf('alternate node ')
0239         end
0240     end
0241 
0242     % Add it to the list of valid rivers
0243     fv_obc(vc) = coast_nodes(idx);
0244 
0245     % We are assuming that the river discharge data array y-dimension is
0246     % ordered the same as the positions in fvcom_xy. If they are not, then
0247     % the discharges for the rivers will be incorrect (i.e. you might put
0248     % the Severn discharge somewhere in the Baltic).
0249     fvcom_names{vc} = sprintf('%07d', ehype_name(ff));
0250     fid = fopen(fullfile(ehype_flow, [fvcom_names{vc}, '.txt']));
0251     assert(fid >= 0, 'Failed to open E-HYPE river flow data.')
0252     if isempty(yr) && ~ceh
0253         % Time series have a 2 line header.
0254         eflow = textscan(fid, '%s %f %f %f %f %f %f %f %f %f', 'delimiter', '\t', 'HeaderLines', 2, 'MultipleDelimsAsOne', 1);
0255     elseif ~isempty(yr) && ceh
0256         eflow = textscan(fid, '%s %f', 'delimiter', ' ', 'MultipleDelimsAsOne', 1);
0257     elseif ~isempty(yr)
0258         % Climatology, so we have no header. Are we using the original
0259         % EHYPE data or some other "time,flux" data?
0260         eflow = textscan(fid, '%s %f %f %f %f %f %f %f %f %f', 'delimiter', '\t', 'MultipleDelimsAsOne', 1);
0261     else
0262         error('Incorrect time set up. Check the ''ceh'' or ''model_time'' keyword-value pairs.')
0263     end
0264     fclose(fid);
0265     % Make sure we always have the right number of time steps. Truncate the
0266     % new data to fit the existing array or wrap the start of the time
0267     % series back to the end.
0268     new_t = length(eflow{2});
0269     % Check if we're doing climatology in which case we can have different
0270     % length time series (for CEH vs. EHYPE derived climatologies, for
0271     % example). If we're not doing climatology, we have to assume the
0272     % modelled time series are all the same length, in which case just use
0273     % the values in new_t.
0274     if ~isempty(yr)
0275         old_t = length(fv_flow(:, 1));
0276     else
0277         old_t = new_t;
0278     end
0279     diff_t = old_t - new_t;
0280     if new_t > old_t
0281         fv_flow(:, vc) = eflow{2}(1:old_t);
0282     elseif new_t < old_t
0283         fv_flow((1:new_t), vc) = eflow{2};
0284         fv_flow(end - diff_t + 1:end, vc) = eflow{2}(1:diff_t);
0285     else
0286         fv_flow(:, vc) = eflow{2};
0287     end
0288     if ftbverbose
0289         fprintf('added (%f, %f)\n', Mobj.lon(fv_obc(vc)), Mobj.lat(fv_obc(vc)))
0290     end
0291 
0292 end
0293 
0294 % Get the length of the EHYPE time series.
0295 ehype_nt = size(fv_flow, 1);
0296 
0297 % Now we've got a list and some of the nodes will be duplicates. Use the
0298 % larger of the two discharge values assigned to those nodes and ditch the
0299 % smaller one. The output is stored in a new fv_uniq_flow array (names and
0300 % nodes are similarly stored in their unique format).
0301 if any(isnan(fv_obc))
0302     % We don't actually have any rivers, so return all the relevant fields
0303     % in Mobj as empty arrays.
0304     Mobj.river_flux = [];
0305     Mobj.river_nodes = [];
0306     Mobj.river_names = [];
0307     Mobj.have_rivers = false;
0308     Mobj.nRivers = 0;
0309 
0310     if ftbverbose
0311         fprintf('end   : %s \n', subname)
0312     end
0313 
0314     return
0315 end
0316 
0317 fv_uniq_obc = unique(fv_obc);
0318 fv_uniq_flow = nan(ehype_nt, length(fv_uniq_obc));
0319 fv_uniq_names = cell(length(fv_uniq_obc), 1);
0320 
0321 fv_idx = 1:length(fvcom_names);
0322 for nn = 1:length(fv_uniq_obc)
0323 
0324     dn = fv_idx(fv_obc == fv_uniq_obc(nn));
0325 
0326     % Instead of summing the values (which causes very large discharges
0327     % because of the way the E-HYPE river mouths are determined), use the
0328     % river flux with the largest mean over the entire time series (~20
0329     % years).
0330     flow_bar = mean(fv_flow(:, dn), 1); % get a mean for each time series
0331     [~, max_idx] = max(flow_bar, [], 2);
0332     fv_uniq_flow(:, nn) = fv_flow(:, dn(max_idx));
0333     fv_uniq_names{nn} = fvcom_names{dn(max_idx)};
0334 
0335     % % This is the old way where nodes which are grouped together are
0336     % % summed. This yielded unrealistically high discharges, particularly at
0337     % % the Fowey near Plymouth. It probably did elsewhere too.
0338     %fv_uniq_flow(:, nn) = sum(fv_flow(:, dn), 2);
0339     % % Concatenate the river names so we know at least which rivers'
0340     % % discharges have been summed.
0341     %s = fvcom_names(dn);
0342     %s = [sprintf('%s-', s{1:end-1}, s{end})];
0343     %fv_uniq_names{nn} = s(1:end-1); % lose the trailing -.
0344 
0345 end
0346 
0347 % Some of the river fluxes are being assigned to adjacent coastal nodes.
0348 % This is no good because we end up putting too much water in (similar
0349 % problem to the multiple river inputs on a single node which is fixed in
0350 % the loop above). So, we need to check each node and check its neighbours
0351 % aren't also rivers. If one (or more?) is, then we need to pick the larger
0352 % discharge (as defined by the mean over the entire time series) and use
0353 % that, removing the other node from the list.
0354 
0355 % Finding the neighbouring nodes is not as straightforward as it might seem
0356 % at first. Simply using poly2cw is no good because our coastline is too
0357 % complicated for that (poly2cw assumes a convex hull). So, we'll find the
0358 % 2 nearest coastline nodes to each river node. If any of those 2 is also a
0359 % river, then merge the two rivers together (use the mean discharge).
0360 
0361 % This breaks down a bit when three rivers are adjacent to one another, but
0362 % most of the time that shouldn't happen...
0363 
0364 % Build a list of the nodes we're considering as neighbouring in
0365 % fv_dups_idx and fv_keep_idx. Store the meaned flow in fv_dups_flow (we'll
0366 % remove the original un-meaned flows at the end). Also store the
0367 % duplicated names in fv_dups_names. All these duplicate arrays will be
0368 % sorted out after the loop to find the adjacent nodes has finished. This
0369 % is less horrible than looping through and adjusting the values in the
0370 % original arrays because the mean of a meaned value and a new value is not
0371 % the same as the mean of the three original values, that is:
0372 %   mean([mean([2, 5]), 10]) ~= mean([2, 5, 10]).
0373 
0374 fv_dups_obc = cell(0);
0375 fv_dups_idx = [];
0376 fv_dups_flow = fv_uniq_flow;
0377 fv_dups_names = cell(0);
0378 fv_uniq_obc_orig = fv_uniq_obc;
0379 c = 0;
0380 for nn = 1:length(fv_uniq_obc)
0381     if isnan(fv_uniq_obc(nn))
0382         % This was already flagged as a pair, so just skip it as we've
0383         % already saved its index.
0384         continue
0385     end
0386     [~, idx] = sort(sqrt(...
0387         (Mobj.x(coast_nodes) - Mobj.x(fv_uniq_obc(nn))).^2 + ...
0388         (Mobj.y(coast_nodes) - Mobj.y(fv_uniq_obc(nn))).^2));
0389     if any(ismember(fv_uniq_obc, coast_nodes(idx(2:3))))
0390         % Build a list of the indices which we want to merge.
0391         fv_dups_idx = [fv_dups_idx, nn];
0392 
0393         c = c + 1;
0394         % Remove the current nodes from the list of river nodes.
0395         fv_dups_obc{c, 1} = fv_uniq_obc(nn);
0396         fv_dups_obc{c, 2} = fv_uniq_obc(ismember(fv_uniq_obc, coast_nodes(idx(2:3))));
0397         fv_uniq_obc(nn) = nan;
0398         fv_uniq_obc(ismember(fv_uniq_obc, coast_nodes(idx(2:3)))) = nan;
0399 
0400         % We can sort out the names and discharges here too. We'll store
0401         % the modified fluxes in a copy of the flux array so we can append
0402         % them once we've cleaned out the duplicate IDs. This way we can
0403         % still get accurate means if we need to reuse a particular node's
0404         % flux. Similarly, merge river names into a separate array.
0405         fv_dups_flow(:, fv_uniq_obc_orig == fv_dups_obc{c, 1}) = ...
0406             mean([fv_uniq_flow(:, fv_uniq_obc_orig == fv_dups_obc{c, 1}), ...
0407             fv_uniq_flow(:, fv_uniq_obc_orig == fv_dups_obc{c, 2})], 2);
0408         fv_dups_names{c} = sprintf('%s-%s', fv_uniq_names{fv_uniq_obc_orig == fv_dups_obc{c, 1}}, ...
0409             fv_uniq_names{fv_uniq_obc_orig == fv_dups_obc{c, 2}});
0410     end
0411 end
0412 
0413 clear c idx
0414 
0415 % Now we can remove the duplicate data from the names, nodes and fluxes.
0416 fv_uniq_obc(fv_dups_idx) = [];
0417 fv_uniq_flow(:, fv_dups_idx) = [];
0418 fv_uniq_names(fv_dups_idx) = [];
0419 fv_uniq_flow(:, isnan(fv_uniq_obc)) = [];
0420 fv_uniq_names(isnan(fv_uniq_obc)) = [];
0421 fv_uniq_obc(isnan(fv_uniq_obc)) = [];
0422 
0423 % And append the averaged flow, names and nodes to the relevant arrays.
0424 fv_uniq_flow = cat(2, fv_uniq_flow, fv_dups_flow(:, fv_dups_idx));
0425 fv_uniq_obc = [fv_uniq_obc, [fv_dups_obc{:, 1}]];
0426 fv_uniq_names = [fv_uniq_names; fv_dups_names'];
0427 
0428 % % Merge the river discharges for the rivers we've identified as adjacent to
0429 % % one another on the coastline.
0430 % assert(mod(numel(fv_dups_obc), 2) ~= 1, 'Odd number of river pairs.')
0431 % assert(mod(length(unique(fv_dups_obc)), 2) ~= 1, 'Duplicate river node in being removed for two separate rivers.')
0432 % nr = length(fv_dups_obc);
0433 % for nn = 1:nr
0434 %     % Find the indices for the flow data for the node to keep and remove.
0435 %     [~, idx1] = find(fv_uniq_obc_orig == fv_dups_obc{nn, 1}); % keep
0436 %     [~, idx2] = find(fv_uniq_obc_orig == fv_dups_obc{nn, 2}); % remove
0437 %     idx = [idx1, idx2]; clear idx1 idx2
0438 %     % Set the first column to the mean of the two rivers and set the other
0439 %     % one to NaN. We'll clear out the NaNs afterwards.
0440 %     fv_uniq_flow(:, idx(1)) = mean(fv_uniq_flow(:, idx), 2);
0441 %     fv_uniq_flow(:, idx(2)) = nan;
0442 %
0443 %     fv_uniq_names{idx(1)} = sprintf('%s-%s', fv_uniq_names{idx(1)}, ...
0444 %         fv_uniq_names{idx(2)});
0445 %     fv_uniq_names{idx(2)} = '';
0446 % end
0447 %
0448 % % Collapse the NaNs out of the flow data; rename the rivers to be
0449 % % hyphenated based on the two source rivers that have been merged.
0450 % nanidx = 1:size(fv_uniq_flow, 2);
0451 % nanidx = nanidx(~isnan(fv_uniq_flow(1, :)));
0452 % fv_uniq_flow = fv_uniq_flow(:, nanidx);
0453 % % Clear out the empty names too.
0454 % c = 0;
0455 % names = cell(0);
0456 % for i = 1:length(fv_uniq_names)
0457 %     if ~isempty(fv_uniq_names{i})
0458 %         c = c + 1;
0459 %         names{c, 1} = fv_uniq_names{i};
0460 %     end
0461 % end
0462 % fv_uniq_names = names;
0463 % clear names
0464 
0465 
0466 % Assign the relevant arrays to the Mobj. Flux is added in the section
0467 % dealing with either climatology or time series data.
0468 Mobj.river_nodes = fv_uniq_obc;
0469 Mobj.river_names = fv_uniq_names;
0470 Mobj.have_rivers = true;
0471 Mobj.nRivers = length(fv_uniq_obc);
0472 
0473 % Create a Modified Julian Day time series of the EHYPE river data. Assume
0474 % all the EHYPE model outputs are for the same period and have the same
0475 % sampling interval. If the eflow{1} data is a number below 367, assume
0476 % we've been given a climatology. In that case, find the model year we're
0477 % using and generate the time string for a year each side of that year (to
0478 % cover the period at each end of a year). For that to work, we need to be
0479 % given the model year as an optional argument to the function.
0480 checkdate = cellfun(@str2num, eflow{1});
0481 if max(checkdate) < 367
0482     % Climatology.
0483     if isempty(yr) && ~isnumeric(yr)
0484         error('For climatology, a year must be specified for the time series to be generated.')
0485     elseif ~isempty(yr) && isnumeric(yr)
0486         % Get to Gregorian first.
0487 
0488         % Make three years of data starting from the year before the
0489         % current one. Do so accounting for leap years.
0490         daysinyr = [sum(eomday(yr - 1, 1:12)), ...
0491             sum(eomday(yr, 1:12)), ...
0492             sum(eomday(yr + 1, 1:12))];
0493         % Offset the checkdate by one to add zero for the first day.
0494         % Alternative would be to specify the day as the end of the
0495         % previous month (or a day value of zero?).
0496         offsetdays = (1:sum(daysinyr)) - 1;
0497         mtime = datevec(datenum(yr - 1, 1, 1, 0, 0, 0) + offsetdays);
0498         Mobj.river_time = greg2mjulian(mtime(:, 1), mtime(:, 2), ...
0499             mtime(:, 3), mtime(:, 4), mtime(:, 5), mtime(:, 6));
0500 
0501         % Repeat the river flux for the climatology before adding to the
0502         % Mobj.
0503         Mobj.river_flux = [...
0504             fv_uniq_flow(1:daysinyr(1), :); ...
0505             fv_uniq_flow(1:daysinyr(2), :); ...
0506             fv_uniq_flow(1:daysinyr(3), :)];
0507     else
0508         error('Non-numeric format for the climatology anchor year.')
0509     end
0510 else
0511     % Time series.
0512     rtimes = datevec(eflow{1});
0513     Mobj.river_time = greg2mjulian(...
0514         rtimes(:, 1), rtimes(:, 2), rtimes(:, 3), ...
0515         rtimes(:, 4), rtimes(:, 5), rtimes(:, 6) ...
0516     );
0517 
0518     % Add the river flux to the Mobj for the time series data.
0519     Mobj.river_flux = fv_uniq_flow;
0520 
0521 end
0522 
0523 if ftbverbose
0524     fprintf('included %d of %d rivers (skipped %d, ignored %d)\n', ...
0525         fv_nr - skipped, fv_nr, skipped, sum(~ignore_mask))
0526     fprintf('end   : %s \n', subname)
0527 end
0528 
0529 % Figure to check what's going on with identifying river nodes
0530 % figure
0531 % plot(ehype_xy(:, 1), ehype_xy(:, 2), '.', 'MarkerFaceColor', 'b')
0532 % hold on
0533 % plot(Mobj.lon(bnd), Mobj.lat(bnd), 'g.', 'MarkerFaceColor', 'g')
0534 % axis('square', 'tight')
0535 % plot(Mobj.lon(coast_nodes), Mobj.lat(coast_nodes), 'r.')
0536 % plot(Mobj.lon(Mobj.river_nodes), Mobj.lat(Mobj.river_nodes), 'k.', 'MarkerFaceColor', 'k')
0537 % % text(Mobj.lon(Mobj.river_nodes) + 0.025, Mobj.lat(Mobj.river_nodes) + 0.025, Mobj.river_names)
0538 % axis([min(Mobj.lon), max(Mobj.lon), min(Mobj.lat), max(Mobj.lat)])
0539 % legend('EHYPE nodes', 'Grid boundary', 'Land nodes', 'Selected nodes', 'Location', 'NorthOutside', 'Orientation', 'Horizontal')
0540 % legend('BoxOff')

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