Home > fvcom_prepro > read_sms_mesh.m

read_sms_mesh

PURPOSE ^

Read sms mesh files into Matlab mesh object.

SYNOPSIS ^

function [Mobj] = read_sms_mesh(varargin)

DESCRIPTION ^

 Read sms mesh files into Matlab mesh object.

 [Mobj] = function read_fvcom_mesh(varargin)

 DESCRIPTION:
    Read SMS 2dm file and bathymetry file
    Store in a matlab mesh object

 INPUT [keyword pairs]:
   '2dm'                   = sms 2dm file [e.g. tst_grd.dat]
   [optional] 'bath'       = sms bathymetry file [e.g. tst_dep.dat]
   [optional] 'coordinate' = coordinate system [spherical; cartesian (default)]
   [optional] 'project'    = generate (x,y) coordinates if input is (lon,lat)
                             generate (lon,lat) coordinates if input is (x,y)
                            ['true' ; 'false' (default)], see myproject.m
   [optional] 'addCoriolis' = calculate Coriolis param (f), requires [lon,lat]

 OUTPUT:
    Mobj = matlab structure containing mesh data

 EXAMPLE USAGE
    Mobj = read_sms_mesh('2dm','skagit.2dm','bath','bathy.dat')

 Author(s):
    Geoff Cowles (University of Massachusetts Dartmouth)
    Pierre Cazenave (Plymouth Marine Laboratory)
    Rory O'Hara Murray (Marine Scotland Science)
    Simon Waldman (Marine Scotland Science / Heriot-Watt University)

 Revision history

   2012-06-20 Add support for reading nodestrings from SMS meshes.
   2012-06-26 Added more resilient support for reading in SMS files.
   2012-06-29 Further improved ability to read files with variable length
   headers.
   2013-07-31 Added some performance improvements to speed up loading mesh
   files (from ~70s to ~30s on a 250,000 node grid). There's probably more
   gains to be had by saving the values of tri, x, y and h when first
   parsing the input file (lines 132-152). My brief testing would suggest
   the overhead of converting from strings to doubles shouldn't be
   underestimated.
   2013-10-01 Further improved ability to read files with variable length
   headers (ROM).
   2013-12-11 Closed the sms_2dm file using fclose (ROM).
   2014-04-10 Fix bugs when not using bathymetry (i.e. only reading the
   grid data in).
   2015-03-19 Add spherical coordinates on element centres.
   2015-09-24 Populate the alternative coordinate system with zeros rather
   than repeating the values. Also add element centre coordinates for
   cartesian coordinates. This is somewhat redundant given setup_metrics
   does this anyway.
   2016-07-28 Fix behaviour if grid has no open boundaries so we can rely
   on have_strings existing in either case.
    [the next few revisions are listed out of order because of rebasing
        a branch that had been separate for a long time]    
   2014-05-29 Changed the way the header is read and skipped (ROM).
   2014-05-29 Changed the way the nodestrings are read, taking into
   account the possibility that SMS adds exatra 'name' number to each
   nodestring after the -ve indicator (ROM).
   2018-05-16 Rewrote nodestring parsing. It's far less elegant, but now
   it still works if the number of nodes in a string is a multiple of 10.
   (SW)
   2018-05-16 If we have bathymetry in the .2dm file *and* a separate
   bathymetry file provided, use the bathymetry in the file (with a
   warning) rather than ignoring it.

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

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [Mobj] = read_sms_mesh(varargin)
0002 % Read sms mesh files into Matlab mesh object.
0003 %
0004 % [Mobj] = function read_fvcom_mesh(varargin)
0005 %
0006 % DESCRIPTION:
0007 %    Read SMS 2dm file and bathymetry file
0008 %    Store in a matlab mesh object
0009 %
0010 % INPUT [keyword pairs]:
0011 %   '2dm'                   = sms 2dm file [e.g. tst_grd.dat]
0012 %   [optional] 'bath'       = sms bathymetry file [e.g. tst_dep.dat]
0013 %   [optional] 'coordinate' = coordinate system [spherical; cartesian (default)]
0014 %   [optional] 'project'    = generate (x,y) coordinates if input is (lon,lat)
0015 %                             generate (lon,lat) coordinates if input is (x,y)
0016 %                            ['true' ; 'false' (default)], see myproject.m
0017 %   [optional] 'addCoriolis' = calculate Coriolis param (f), requires [lon,lat]
0018 %
0019 % OUTPUT:
0020 %    Mobj = matlab structure containing mesh data
0021 %
0022 % EXAMPLE USAGE
0023 %    Mobj = read_sms_mesh('2dm','skagit.2dm','bath','bathy.dat')
0024 %
0025 % Author(s):
0026 %    Geoff Cowles (University of Massachusetts Dartmouth)
0027 %    Pierre Cazenave (Plymouth Marine Laboratory)
0028 %    Rory O'Hara Murray (Marine Scotland Science)
0029 %    Simon Waldman (Marine Scotland Science / Heriot-Watt University)
0030 %
0031 % Revision history
0032 %
0033 %   2012-06-20 Add support for reading nodestrings from SMS meshes.
0034 %   2012-06-26 Added more resilient support for reading in SMS files.
0035 %   2012-06-29 Further improved ability to read files with variable length
0036 %   headers.
0037 %   2013-07-31 Added some performance improvements to speed up loading mesh
0038 %   files (from ~70s to ~30s on a 250,000 node grid). There's probably more
0039 %   gains to be had by saving the values of tri, x, y and h when first
0040 %   parsing the input file (lines 132-152). My brief testing would suggest
0041 %   the overhead of converting from strings to doubles shouldn't be
0042 %   underestimated.
0043 %   2013-10-01 Further improved ability to read files with variable length
0044 %   headers (ROM).
0045 %   2013-12-11 Closed the sms_2dm file using fclose (ROM).
0046 %   2014-04-10 Fix bugs when not using bathymetry (i.e. only reading the
0047 %   grid data in).
0048 %   2015-03-19 Add spherical coordinates on element centres.
0049 %   2015-09-24 Populate the alternative coordinate system with zeros rather
0050 %   than repeating the values. Also add element centre coordinates for
0051 %   cartesian coordinates. This is somewhat redundant given setup_metrics
0052 %   does this anyway.
0053 %   2016-07-28 Fix behaviour if grid has no open boundaries so we can rely
0054 %   on have_strings existing in either case.
0055 %    [the next few revisions are listed out of order because of rebasing
0056 %        a branch that had been separate for a long time]
0057 %   2014-05-29 Changed the way the header is read and skipped (ROM).
0058 %   2014-05-29 Changed the way the nodestrings are read, taking into
0059 %   account the possibility that SMS adds exatra 'name' number to each
0060 %   nodestring after the -ve indicator (ROM).
0061 %   2018-05-16 Rewrote nodestring parsing. It's far less elegant, but now
0062 %   it still works if the number of nodes in a string is a multiple of 10.
0063 %   (SW)
0064 %   2018-05-16 If we have bathymetry in the .2dm file *and* a separate
0065 %   bathymetry file provided, use the bathymetry in the file (with a
0066 %   warning) rather than ignoring it.
0067 %
0068 %==============================================================================
0069 
0070 [~, subname] = fileparts(mfilename('fullpath'));
0071 global ftbverbose;
0072 if ftbverbose
0073     fprintf('\nbegin : %s \n', subname)
0074 end
0075 
0076 userproject = false;
0077 have_bath = false;
0078 have_strings = false;
0079 
0080 %--------------------------------------------------------------------------
0081 % Create a blank mesh object
0082 %--------------------------------------------------------------------------
0083 Mobj = make_blank_mesh;
0084 coordinate = 'cartesian';
0085 
0086 %--------------------------------------------------------------------------
0087 % Parse input arguments
0088 %--------------------------------------------------------------------------
0089 
0090 if mod(length(varargin), 2) ~= 0
0091     error('incorrect usage of read_sms_mesh, use keyword pairs')
0092 end
0093 
0094 for i = 1:2:length(varargin) - 1
0095     keyword = lower(varargin{i});
0096 
0097     if ~ischar(keyword)
0098         error('incorrect usage of read_sms_mesh')
0099     end
0100 
0101     switch keyword
0102         case '2dm'
0103             sms_2dm = varargin{i + 1};
0104             have_2dm = true;
0105         case 'bath'
0106             sms_bath = varargin{i + 1};
0107             have_bath = true;
0108         case 'coordinate'
0109             coord = varargin{i + 1};
0110             if strcmpi(coord, 'spherical')
0111                 coordinate = 'spherical';
0112                 have_lonlat = true;
0113             elseif strcmpi(coord, 'cartesian')
0114                 coordinate = 'cartesian';
0115                 have_xy = true;
0116             else
0117                 warning('Unrecognised coordinate system (%s). Valid values are ''spherical'' and ''cartesian''.', coordinate)
0118             end
0119         case 'project'
0120             val = varargin{i + 1};
0121             if val
0122                 userproject = true;
0123             else
0124                 userproject = false;
0125             end
0126         case 'addcoriolis'
0127             val = varargin{i + 1};
0128             if val
0129                 addCoriolis = true;
0130             else
0131                 addCoriolis = false;
0132             end
0133         otherwise
0134             disp(varargin{i + 1})
0135             error('Can''t understand property: %s', varargin{i + 1});
0136 
0137     end
0138 end
0139 
0140 %--------------------------------------------------------------------------
0141 % Read the mesh from the 2dm file
0142 %--------------------------------------------------------------------------
0143 
0144 fid = fopen(sms_2dm, 'rt');
0145 if fid < 0
0146     error(['file: ' sms_2dm ' does not exist']);
0147 end
0148 
0149 % Count number of elements and vertices
0150 if ftbverbose
0151     fprintf('reading from: %s\n', sms_2dm)
0152     fprintf('first pass to count number of nodes and vertices\n')
0153 end
0154 
0155 nElems = 0;
0156 nVerts = 0;
0157 nStrings = 0;
0158 nHeader = 0;
0159 StillReading = true;
0160 while StillReading
0161     lin = fgetl(fid);
0162     if lin ~= -1 % EOF is -1
0163         switch lin(1:2)
0164             case 'E3'
0165                 nElems = nElems + 1;
0166             case 'ND'
0167                 nVerts = nVerts + 1;
0168             case 'NS'
0169                 nStrings = nStrings + 1;
0170             case {'ME', 'NU'}
0171                 nHeader = nHeader + 1;
0172             case 'E4'
0173                 error('Quadrilateral elements are unsupported in FVCOM')
0174             otherwise
0175                 StillReading = false;
0176         end
0177     else
0178         % Got to EOF
0179         StillReading = false;
0180     end
0181 end
0182 fclose(fid);
0183 
0184 fid = fopen(sms_2dm, 'rt');
0185 
0186 if ftbverbose
0187   fprintf('nVerts: %d\n', nVerts);
0188   fprintf('nElems: %d\n', nElems);
0189   fprintf('reading in connectivity and grid points\n')
0190 end
0191 
0192 % Allocate memory to hold mesh and connectivity
0193 tri = zeros(nElems,3);
0194 lon = zeros(nVerts,1);
0195 lat = zeros(nVerts,1);
0196 ts  = zeros(nVerts,1);
0197 
0198 % Skip the header
0199 for ii=1:nHeader
0200     lin = fgetl(fid);
0201 end
0202 
0203 % Read the triangulation table
0204 C = textscan(fid, '%s %d %d %d %d %d', nElems);
0205 tri(:, 1) = C{3};
0206 tri(:, 2) = C{4};
0207 tri(:, 3) = C{5};
0208 
0209 % Read in the nodes and interpolated depths
0210 C = textscan(fid, '%s %d %f %f %f ', nVerts);
0211 x = C{3};
0212 y = C{4};
0213 h = C{5};
0214 
0215 % Check we don't have any NaNs anywhere
0216 if max(isnan(x)) == 1
0217     error('%d NaNs in the x data', sum(isnan(x)))
0218 end
0219 if max(isnan(y)) == 1
0220     error('%d NaNs in the y data', sum(isnan(x)))
0221 end
0222 if max(isnan(h)) == 1
0223     error('%d NaNs in the h data', sum(isnan(x)))
0224 end
0225 if max(isnan(tri(:))) == 1
0226     error('%d NaNs in the h data', sum(isnan(tri(:))))
0227 end
0228 
0229 %Read in nodestrings.
0230 tmp = textscan(fid, ['%s' repmat('%d', 1, 12) '%*[^\n]'], nStrings, 'delimiter', ' ', 'MultipleDelimsAsOne', 1, 'CollectOutput', 1);
0231 % this allows for up to 12 items on a NS line. It's normally 10, but can
0232 % sometimes be 11. If we hit 12, something's changed in SMS's output.
0233 % The second cell of the cell array returned by this should be a matrix of all the numeric
0234 % values. Columns that don't have values in the file will contain 0.
0235 mNSlines = tmp{2};
0236 clear tmp;
0237 
0238 % We'll work through the rows of this matrix and assemble the
0239 % nodestring(s).
0240 currentNSno = 1;
0241 currentNS = [];
0242 NSlengths = [];
0243 for r = 1:size(mNSlines,1)  %rows
0244     for c = 1:size(mNSlines, 2) %columns
0245         if mNSlines(r,c)==0 %we've run out of values on this line. Skip to next line.
0246             break;
0247         elseif mNSlines(r,c) < 0
0248             %end of nodestring, marked by a negative node number.
0249             %Append positive value to the NS, do end-of-NS stuff, then ignore
0250             %rest of line.
0251             currentNS = [currentNS abs(mNSlines(r,c))];
0252             % Remove duplicate nodestring IDs in case we have them.
0253             read_obc_nodes{currentNSno} = unique(currentNS, 'stable');
0254             NSlengths = [NSlengths length(currentNS)];
0255             currentNSno = currentNSno + 1;
0256             currentNS = [];
0257             break;
0258         else
0259             % append value to nodestring. If this is in a column higher
0260             % than 10, raise a warning, as SMS doesn't usually do this.
0261             currentNS = [currentNS mNSlines(r,c)];
0262             if c > 10
0263                 warning('Longer lines than expected when parsing nodestrings; SMS output may not be as expected. Run with ftbverbose=true and check that the number and length of nodestrings found is as expected.');
0264             end
0265         end
0266     end
0267 end
0268 
0269 if ftbverbose
0270     a = sprintf('%d ', NSlengths);
0271     fprintf('%i complete nodestrings found, of lengths %s. \n', currentNSno - 1, a);
0272     clear a
0273 end
0274 
0275 if nStrings > 0
0276     have_strings = true;
0277 end
0278 
0279 have_lonlat = false;
0280 have_xy     = false;
0281 if strcmpi(coordinate, 'spherical')
0282     lon = x;
0283     lat = y;
0284     % Why reset everything to zero here?
0285     %x = x * 0.0;
0286     %y = y * 0.0;
0287     have_lonlat = true;
0288     % Just do a double check on the coordinates to make sure we don't
0289     % actually have cartesian
0290     if max(lon) > 360
0291         warning('You''ve specified spherical coordinates, but your upper longitude value exceeds 360 degrees. Are you sure you have spherical data?')
0292     end
0293 elseif strcmpi(coordinate, 'cartesian')
0294     have_xy = true;
0295 else
0296     warning('Unrecognised coordinate system (%s). Valid values are ''spherical'' and ''cartesian''.', coordinate)
0297 end
0298 
0299 fclose(fid);
0300 
0301 %--------------------------------------------------------------------------
0302 % Read the topography from the bathymetry file
0303 %--------------------------------------------------------------------------
0304 
0305 bath_range = max(h) - min(h);
0306 if have_bath
0307     if bath_range ~= 0
0308         warning(['Bathymetry is present in the .2dm file, but a bathymetry .dat file was also provided. '...
0309             'The .dat file will be used, and the depth info in the .2dm will be ignored.']);
0310     end
0311     fid = fopen(sms_bath, 'rt');
0312     if fid < 0
0313         error('file: %s does not exist', sms_bath);
0314     else
0315         if ftbverbose; fprintf('reading sms bathymetry from: %s\n', sms_bath); end
0316     end
0317     lin = fgetl(fid);
0318     lin = fgetl(fid);
0319     lin = fgetl(fid);
0320     C = textscan(fid, '%s %d', 1);
0321     nVerts_tmp = C{2};
0322     C = textscan(fid, '%s %d', 1);
0323     nElems_tmp = C{2};
0324     if (nVerts - nVerts_tmp) * (nElems - nElems_tmp) ~= 0
0325         fprintf('dimensions of bathymetry file do not match 2dm file\n')
0326         fprintf('bathymetry nVerts: %d\n',nVerts_tmp)
0327         fprintf('bathymetry nElems: %d\n',nElems_tmp)
0328         error('stopping...')
0329     end
0330     lin = fgetl(fid);
0331     lin = fgetl(fid);
0332     lin = fgetl(fid);
0333     lin = fgetl(fid); % extra one for the new format from SMS 10.1, I think
0334     C2 = textscan(fid, '%f', nVerts);
0335     h = C2{1};
0336     have_bath = true;
0337     
0338     clear C2
0339     
0340     fclose(fid);
0341     
0342 elseif bath_range ~= 0
0343     have_bath = true;
0344 end
0345 
0346 % Make sure we have positive depths
0347 if sum(h > 0) < sum(h < 0)
0348     h = -h;
0349 end
0350 
0351 %--------------------------------------------------------------------------
0352 % Project if desired by user
0353 %--------------------------------------------------------------------------
0354 if userproject
0355     if strcmpi(coordinate, 'cartesian')
0356         fprintf('inverse projecting to get (lon,lat)\n')
0357         [lon, lat] = my_project(x, y, 'inverse');
0358         have_lonlat = true;
0359     elseif strcmpi(coordinate, 'spherical')
0360         fprintf('forward projecting to get (x,y)\n')
0361         [x, y] = my_project(lon, lat, 'forward');
0362         have_xy = true;
0363     else
0364         warning('Unrecognised coordinate system (%s). Valid values are ''spherical'' and ''cartesian''.', coordinate)
0365     end
0366 end
0367 
0368 %--------------------------------------------------------------------------
0369 % Transfer to Mesh structure
0370 %--------------------------------------------------------------------------
0371 
0372 Mobj.nVerts  = nVerts;
0373 Mobj.nElems  = nElems;
0374 Mobj.nativeCoords = coordinate;
0375 
0376 Mobj.ts           = ts;
0377 Mobj.h            = h;
0378 Mobj.tri          = tri;
0379 
0380 if have_lonlat
0381     Mobj.have_lonlat    = have_lonlat;
0382     Mobj.lon            = lon;
0383     Mobj.lat            = lat;
0384     if ~have_xy
0385         Mobj.x              = zeros(size(lon));
0386         Mobj.y              = zeros(size(lat));
0387     end
0388     % Add element spherical coordinates too.
0389     Mobj.lonc = nodes2elems(lon, Mobj);
0390     Mobj.latc = nodes2elems(lat, Mobj);
0391 end
0392 if have_xy
0393     Mobj.have_xy        = have_xy;
0394     Mobj.x              = x;
0395     Mobj.y              = y;
0396     if ~have_lonlat
0397         Mobj.lon            = zeros(size(x));
0398         Mobj.lat            = zeros(size(y));
0399     end
0400     % Add element cartesian coordinates too.
0401     Mobj.xc = nodes2elems(x, Mobj);
0402     Mobj.yc = nodes2elems(y, Mobj);
0403 end
0404 if have_bath
0405     Mobj.have_bath      = have_bath;
0406 end
0407 if have_strings
0408     Mobj.have_strings   = have_strings;
0409     Mobj.read_obc_nodes = read_obc_nodes;
0410 else
0411     Mobj.have_strings   = false;
0412 end
0413 if exist('addCoriolis', 'var') && addCoriolis
0414     Mobj.have_cor       = true;
0415 end
0416 
0417 assert(isfield(Mobj, 'x'), 'No coordinate data provided. Check your inputs and try again.')
0418 
0419 % Make a depth array for the element centres.
0420 Mobj.hc = nodes2elems(h, Mobj);
0421 
0422 % Add element spherical coordinates too.
0423 Mobj.lonc = nodes2elems(lon, Mobj);
0424 Mobj.latc = nodes2elems(lat, Mobj);
0425 
0426 %--------------------------------------------------------------------------
0427 % Add the Coriolis values
0428 %--------------------------------------------------------------------------
0429 if exist('addCoriolis', 'var') && addCoriolis
0430     Mobj = add_coriolis(Mobj, 'uselatitude');
0431 end
0432 
0433 if ftbverbose
0434   fprintf('end   : %s\n', subname)
0435 end

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