


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.
==========================================================================

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')