


Extract tidal harmonic phases and amplitudes from POLPRED ASCII files.
get_POLPRED_spectide(Mobj, POLPRED)
DESCRIPTION:
Extract POLPRED harmonic amplitude and phases for the nearest point in
the POLPRED grid to the open boundary nodes in Mobj.
INPUT:
Mobj = MATLAB mesh object (see read_sms_mesh.m), with fields:
- have_lonlat - flag indicating spherical coordinates
- obc_nodes - open boundary node IDs
- lon - longitude values
- lat - latitude values
POLPRED = ASCII file path of the POLPRED harmonics
OUTPUT:
Mobj = MATLAB mesh object with two new arrays:
phase - Harmonic phases at each open boundary point
amp - Harmonic amplitudes at each open boundary point
EXAMPLE USAGE
Mobj = get_POLPRED_spectide(Mobj, '/path/to/POLPRED.txt')
Author(s):
Pierre Cazenave (Plymouth Marine Laboratory)
Revision history
2012-11-15 First version. Based in part on tide_tools.py from the
PyFVCOM Python toolbox (https://gitlab.em.pml.ac.uk/pica/PyFVCOM)
and Ricardo Torres' searchtides.m.
==========================================================================

0001 function [Mobj] = get_POLPRED_spectide(Mobj, POLPRED) 0002 % Extract tidal harmonic phases and amplitudes from POLPRED ASCII files. 0003 % 0004 % get_POLPRED_spectide(Mobj, POLPRED) 0005 % 0006 % DESCRIPTION: 0007 % Extract POLPRED harmonic amplitude and phases for the nearest point in 0008 % the POLPRED grid to the open boundary nodes in Mobj. 0009 % 0010 % INPUT: 0011 % Mobj = MATLAB mesh object (see read_sms_mesh.m), with fields: 0012 % - have_lonlat - flag indicating spherical coordinates 0013 % - obc_nodes - open boundary node IDs 0014 % - lon - longitude values 0015 % - lat - latitude values 0016 % POLPRED = ASCII file path of the POLPRED harmonics 0017 % 0018 % OUTPUT: 0019 % Mobj = MATLAB mesh object with two new arrays: 0020 % phase - Harmonic phases at each open boundary point 0021 % amp - Harmonic amplitudes at each open boundary point 0022 % 0023 % EXAMPLE USAGE 0024 % Mobj = get_POLPRED_spectide(Mobj, '/path/to/POLPRED.txt') 0025 % 0026 % Author(s): 0027 % Pierre Cazenave (Plymouth Marine Laboratory) 0028 % 0029 % Revision history 0030 % 2012-11-15 First version. Based in part on tide_tools.py from the 0031 % PyFVCOM Python toolbox (https://gitlab.em.pml.ac.uk/pica/PyFVCOM) 0032 % and Ricardo Torres' searchtides.m. 0033 % 0034 %========================================================================== 0035 0036 subname = 'get_POLPRED_spectide'; 0037 0038 global ftbverbose; 0039 if ftbverbose 0040 fprintf('\n') 0041 fprintf(['begin : ' subname '\n']) 0042 end 0043 0044 % Check we have spherical coordinates in Mobj (we can't extract harmonics 0045 % without them (easily)). 0046 if ~Mobj.have_lonlat 0047 error('Spherical coordinates absent from Mobj. Cannot extract harmonics from cartesian coordinates.') 0048 end 0049 0050 % Read the POLPRED header into a struct of header names plus their values. 0051 fid = fopen(POLPRED,'rt'); 0052 if(fid < 0) 0053 error(['file: ' POLPRED ' does not exist']); 0054 end 0055 0056 if ftbverbose 0057 fprintf(['reading from: ' POLPRED '\n']) 0058 fprintf('extracting header\n') 0059 end 0060 0061 readingHeader = true; 0062 header = struct(); 0063 while readingHeader 0064 lin = fgetl(fid); 0065 if isempty(lin) 0066 % Got to the end of the header 0067 readingHeader = false; 0068 else 0069 % We have some header information. Load it into a struct. 0070 key = regexp(lin, ':', 'split'); 0071 header.(strtrim(regexprep(key{1}, ' ', '_'))) = strtrim(key{2}); 0072 end 0073 end 0074 0075 % Reformat the list of harmonics into a more sensible format 0076 header.Harmonics = regexp(header.Harmonics, ' ', 'split'); 0077 0078 % Get the positions in header.Harmonics for the constituents in which we're 0079 % interested. 0080 0081 pInd = 1:length(header.Harmonics); 0082 pIndUse = nan(length(Mobj.Components), 2); 0083 for i=1:length(Mobj.Components) 0084 pPos = pInd(strcmp(Mobj.Components{i}, header.Harmonics)); 0085 if isempty(pPos) 0086 warning('Supplied constituent (%s) is not present in the POLPRED data', Mobj.Components{i}) %#ok<WNTAG> 0087 else 0088 % Make index start at zero so the multiplication works, but 0089 % compensate for that once the offset has been applied. Also add 0090 % offset for the 2 columns (amplitude and phase). 0091 pIndUse(i, :) = (repmat((pPos - 1 ) * 6, 1, 2) + 1) + (0:1); 0092 end 0093 end 0094 % Add three to offset by the lat, lon and flag columns 0095 pIndUse = pIndUse + 3; 0096 0097 % Now we're at the data. Load it all into a massive array. 0098 if ftbverbose 0099 fprintf('extracting data\n') 0100 end 0101 0102 readingData = true; 0103 i = 0; 0104 % Preallocate data to something big and then cut back afterwards (if 0105 % necessary). Get the number of columns from the header and multiply by 6 0106 % (amplitude and phases for z, u and v). Add three for the lat, lon and 0107 % flag columns). The rows is the number of data lines in my files for the 0108 % northwest European shelf domain. 0109 nCols = 3 + (str2double(header.Number_of_harmonics) * 6); 0110 data = nan(358797, nCols); 0111 if ftbverbose 0112 tic 0113 end 0114 while readingData 0115 lin = fgetl(fid); 0116 if lin ~= -1 % EOF is -1 0117 i = i + 1; 0118 if ftbverbose 0119 if mod(i, 10000) == 0 0120 fprintf('line %i\n', i) 0121 end 0122 end 0123 % str2double doesn't work without a couple of calls to regexp, 0124 % which makes it ~20x slower than str2num on its own. The regexp 0125 % approach is still here if you don't believe me. 0126 data(i, :) = str2num(strtrim(lin)); %#ok<ST2NM> 0127 % data(i, :) = str2double(regexp(regexprep(strtrim(lin), '\s+', ' '), ' ', 'split')); 0128 else 0129 if ftbverbose 0130 fprintf('end of file at line %i\n', i) 0131 end 0132 readingData = false; 0133 end 0134 end 0135 if ftbverbose 0136 toc 0137 end 0138 0139 fclose(fid); 0140 0141 % Clear out any additional NaNs in data from preallocation. 0142 data = reshape(data(~isnan(data)), i, nCols); 0143 0144 % Now we have the data, identify the indices of data which correspond to 0145 % the nearest point to each open boundary point. This approach may not be 0146 % the best: it might instead be better to simply read in the positions and 0147 % create an index which we use to extract the harmonics of interest. 0148 % However, we've got this far so might as well carry on. 0149 0150 % Get the open boundary node IDs with which to extract their locations 0151 tmpObcNodes = Mobj.obc_nodes'; 0152 ObcNodes = tmpObcNodes(tmpObcNodes~=0)'; 0153 obc_lon = Mobj.lon(ObcNodes); 0154 obc_lat = Mobj.lat(ObcNodes); 0155 0156 % For each position, find the nearest POLPRED value. Use the 0157 % find_nearest_pt.m logic to get the nearest point (we can't use the 0158 % function here because the values for which we're searching aren't in 0159 % Mobj). 0160 distance = nan(size(obc_lon)); 0161 point = nan(size(distance)); 0162 % Omit the NaNs in the indices from POLPRED when calculating the output 0163 % array size. 0164 amp = nan(length(obc_lon), length(pIndUse(~isnan(pIndUse(:, 1)), 1))); 0165 phase = nan(size(amp)); 0166 for i=1:length(obc_lon) 0167 radvec = sqrt((obc_lon(i)-data(:,2)).^2 + (obc_lat(i)-data(:,1)).^2); 0168 [distance(i), point(i)] = min(radvec); 0169 % Get the amplitude and phase for each constituent (in order of 0170 % Mobj.Components). Check for and omit NaNs here (for missing tidal 0171 % constituents in the supplied list and what's given in POLPRED). 0172 amp(i, :) = data(point(i), pIndUse(~isnan(pIndUse(:, 1)), 1)); 0173 phase(i, :) = data(point(i), pIndUse(~isnan(pIndUse(:, 1)), 2)); 0174 end 0175 0176 % Check for and warn about NaNs (-999.9 in POLPRED data). 0177 if sum(amp(:)==-999.9) > 0 0178 warning('NaN values (-999.9 in POLPRED terms) in the amplitude data. Are your boundaries on land?') %#ok<WNTAG> 0179 end 0180 if sum(phase(:)==-999.9) > 0 0181 warning('NaN values (-999.9 in POLPRED terms) in the phase data. Are your boundaries on land?') %#ok<WNTAG> 0182 end 0183 0184 Mobj.amp = amp; 0185 Mobj.phase = phase; 0186 0187 % Plot the open boundary positions and the closest POLPRED point. 0188 % figure(1000) 0189 % plot(obc_lon, obc_lat, 'o') 0190 % hold on 0191 % plot(data(point,2), data(point,1), 'rx') 0192