


Read river temperature climatologies from the Environment Agency river
temperature data. If no data are found within the threshold specified, a
mean climatology from the nearest 30 sites is provided instead.
function Mobj = get_EA_river(Mogj, ea)
DESCRIPTION:
Load all the river data from the river climatology netCDF file and find
the most relevant one to the river nodes in Mobj.rivers.positions.
INPUT:
Mobj : MATLAB mesh structure which must contain:
- Mobj.river_nodes - river node IDs.
- Mobj.lon, Mobj.lat - unstructured grid node
positions.
- Mobj.river_time - Modified Julian Day array of the
times for the river discharge data (Mobj.river_flux).
ea : Full path to the river climatology netCDF file.
dist_thresh : distance threshold beyond which a river temperature
climatology data point is considered too far to be valid
(in degrees).
OUTPUT:
Mobj : MATLAB structure with a new Mobj.river_temp field which
contains the climatology for the river nodes.
EXAMPLE USAGE:
Mobj = get_EA_river_climatology(Mobj, '/path/to/netcdf.nc', 0.05)
Author(s):
Pierre Cazenave (Plymouth Marine Laboratory)
Revision history
2013-11-05 First version.
2014-07-08 Think I've fixed the issue with leap years and incorrectly
sized output temperature arrays with multiple years.
2014-09-02 Nope, I hadn't fixed leap years, but I might have now.
2015-09-24 Add check for whether we actually have any rivers to
process.

0001 function Mobj = get_EA_river_climatology(Mobj, ea, dist_thresh) 0002 % Read river temperature climatologies from the Environment Agency river 0003 % temperature data. If no data are found within the threshold specified, a 0004 % mean climatology from the nearest 30 sites is provided instead. 0005 % 0006 % function Mobj = get_EA_river(Mogj, ea) 0007 % 0008 % DESCRIPTION: 0009 % Load all the river data from the river climatology netCDF file and find 0010 % the most relevant one to the river nodes in Mobj.rivers.positions. 0011 % 0012 % INPUT: 0013 % Mobj : MATLAB mesh structure which must contain: 0014 % - Mobj.river_nodes - river node IDs. 0015 % - Mobj.lon, Mobj.lat - unstructured grid node 0016 % positions. 0017 % - Mobj.river_time - Modified Julian Day array of the 0018 % times for the river discharge data (Mobj.river_flux). 0019 % ea : Full path to the river climatology netCDF file. 0020 % dist_thresh : distance threshold beyond which a river temperature 0021 % climatology data point is considered too far to be valid 0022 % (in degrees). 0023 % 0024 % OUTPUT: 0025 % Mobj : MATLAB structure with a new Mobj.river_temp field which 0026 % contains the climatology for the river nodes. 0027 % 0028 % EXAMPLE USAGE: 0029 % Mobj = get_EA_river_climatology(Mobj, '/path/to/netcdf.nc', 0.05) 0030 % 0031 % Author(s): 0032 % Pierre Cazenave (Plymouth Marine Laboratory) 0033 % 0034 % Revision history 0035 % 2013-11-05 First version. 0036 % 2014-07-08 Think I've fixed the issue with leap years and incorrectly 0037 % sized output temperature arrays with multiple years. 0038 % 2014-09-02 Nope, I hadn't fixed leap years, but I might have now. 0039 % 2015-09-24 Add check for whether we actually have any rivers to 0040 % process. 0041 0042 subname = 'get_EA_river_climatology'; 0043 0044 global ftbverbose 0045 if ftbverbose 0046 fprintf('\nbegin : %s \n', subname) 0047 end 0048 0049 if isempty(Mobj.river_nodes) 0050 warning('No rivers specified in the domain.') 0051 Mobj.river_temp = []; 0052 0053 if ftbverbose 0054 fprintf('end : %s\n', subname) 0055 end 0056 return 0057 end 0058 0059 % Load the position (lon/lat), time, climatology and SiteType variables 0060 % only. Not really bothered about the other variables. 0061 nc = netcdf.open(ea, 'NOWRITE'); 0062 varid = netcdf.inqVarID(nc, 'climatology'); 0063 climatology = netcdf.getVar(nc, varid, 'single'); 0064 varid = netcdf.inqVarID(nc, 'time'); 0065 time = netcdf.getVar(nc, varid, 'single'); 0066 varid = netcdf.inqVarID(nc, 'lon'); 0067 lon = netcdf.getVar(nc, varid, 'single'); 0068 varid = netcdf.inqVarID(nc, 'lat'); 0069 lat = netcdf.getVar(nc, varid, 'single'); 0070 varid = netcdf.inqVarID(nc, 'SiteType'); 0071 SiteType = netcdf.getVar(nc, varid); 0072 netcdf.close(nc) 0073 clear varid 0074 0075 % Remove any sites which aren't RIVER in SiteType. This is not pretty but 0076 % relatively speedy, so it'll have to do. 0077 good = []; 0078 for i = 1:size(SiteType, 2) 0079 if strcmp(strtrim(SiteType(:, i)'), 'RIVER') 0080 good = [good, i]; 0081 end 0082 end 0083 0084 % Clear out the bad sites. 0085 climatology = climatology(:, good); 0086 lon = lon(good); 0087 lat = lat(good); 0088 0089 % Now find the nearest nodes to the river node positions. 0090 nr = length(Mobj.river_nodes); 0091 0092 clim = nan(length(time), nr); 0093 0094 for r = 1:nr 0095 dist = sqrt((lon - Mobj.lon(Mobj.river_nodes(r))).^2 + (lat - Mobj.lat(Mobj.river_nodes(r))).^2); 0096 [howclose, idx] = min(dist); 0097 0098 if howclose > dist_thresh 0099 % Find the 30 closest sites and use their data instead. 0100 [~, idx] = sort(dist); 0101 clim(:, r) = median(climatology(:, idx(1:30)), 2); 0102 else 0103 % Get the relevant climatology. 0104 clim(:, r) = climatology(:, idx); 0105 end 0106 end 0107 0108 % Since the EA climatology is only 365 days long, add the mean of 0109 % the first and last days to the end of the time series for leap 0110 % years. 0111 clim = [clim; mean([clim(1, :); clim(end, :)])]; 0112 0113 % Now we have the climatologies for the relevant river nodes, we need to 0114 % repeat it to fit the length of the Mobj.river_time array. Since the 0115 % climatology data are for a year only, we need to find the correct index 0116 % for the start and end of the Mobj.river_time array so that we don't put 0117 % January temperature in July, for example. 0118 [yyyy, mm, dd, HH, MM, SS] = mjulian2greg(Mobj.river_time); 0119 startday = (datenum(yyyy(1), mm(1), dd(1), HH(1), MM(1), SS(1)) - ... 0120 datenum(min(yyyy), 1, 1, 0, 0, 0)) + 1; % add offset of 1 for MATLAB indexing. 0121 warning('Don''t know what''s going on with this here. Check the code to find the end day for the river climatology.') 0122 0123 years = unique(yyyy); 0124 ny = length(years); 0125 if ny == 1 0126 % Offset time series by 1 day to get a full year (365/366 days). 0127 endday = (1 + datenum(yyyy(end), mm(end), dd(end), HH(end), MM(end), SS(end)) - ... 0128 datenum(max(yyyy), 1, 1, 0, 0, 0)); 0129 0130 % Subset the river climatology for the right days. 0131 repclim = clim(startday:endday, :); 0132 else 0133 % Otherwise build up the time series using the number of unique years 0134 % we have. 0135 for y = 1:ny 0136 % Find the number of days in this year and only extract that number 0137 % from the climatology. 0138 tidx = 1:length(yyyy); 0139 tidx(yyyy ~= years(y)) = []; 0140 % Offset time series by 1 day to get a full year (365/366 days). 0141 endday = (1 + datenum(yyyy(tidx(end)), mm(tidx(end)), dd(tidx(end)), HH(tidx(end)), MM(tidx(end)), SS(tidx(end))) - ... 0142 datenum(max(yyyy(tidx)), 1, 1, 0, 0, 0)); 0143 0144 nd = sum(eomday(years(y), 1:12)); 0145 if y == 1 0146 % This is the part year for the first year. Prepend the 0147 % existing array with the number of days required. 0148 repclim = clim(startday:end, :); 0149 elseif y == ny 0150 repclim = [repclim; clim(1:endday, :)]; 0151 elseif y ~= 1 || y ~= ny 0152 % We're in the middle years, so just repeat add the clim array 0153 % to the end of the previous interation's. 0154 repclim = [repclim; clim]; 0155 end 0156 0157 % We sometimes need to add an extra couple of day's data to the end 0158 % of the array for this (leap) year. A recent fix to the code above 0159 % should have made this obsolete, but you never know... 0160 if nd == 366 0161 backidx = 366 - nd; 0162 repclim = [repclim; repclim(end - backidx:end, :)]; 0163 end 0164 end 0165 end 0166 0167 % Add the temperature climatology to Mobj. 0168 Mobj.river_temp = repclim; 0169 0170 if ftbverbose 0171 fprintf('end : %s \n', subname) 0172 end