function mat2roms_rps(theMatFile, theROMSFile) % mat2roms_rps -- Convert from Mat-file to ROMS file. % mat2roms('theMatFile', 'theROMSFile') converts % 'theMatFile' (mostly RHO information) to 'theROMSFile'. % % NOTE: This is a modified version of Chuck Denham's mat2roms function. % In this version the only requirements for the input mat file are: % s.rho.lon % s.rho.lat % s.rho.depth, where (depth == nan) indicates land points % % The projected coordinate is calculated via the projection % specified in this routine. This is *only* used to interpolate % the grid to find the lon/lat values of the u,v and psi % points and to write out x_rho, y_rho, x_u, y_u, etc, which % are not used by ROMS, but could be useful for % plotting. The only important quantities for ROMS are the % metric factors pm and pn and the angles, which % are calculated from the lon/lat values % assuming a spherical earth, not from distances in the % projected coordinates. % % -Rich Signell (21-May-2003) rsignell@usgs.gov % % This routine calls m_proj from the m_map toolkit and % also sw_dist and sw_f from the seawater toolkit (both % available at http://sea-mat.whoi.edu % Copyright (C) 2002 Dr. Charles R. Denham, ZYDECO. % All Rights Reserved. % Disclosure without explicit written consent from the % copyright owner does not constitute publication. % Version of 22-May-2002 16:25:10. % Updated 17-Jun-2002 16:49:32. % Updated by RPS 21-May-2003 isMacintosh = ~isunix & any(findstr(lower(computer), 'mac')); if nargin < 1, theMatFile = '*.mat'; end if nargin < 2, theROMSFile = 'roms_model_grid.nc'; end % Get the file names. if any(theMatFile == '*') help(mfilename) theFilterSpec = theMatFile; thePrompt = 'Select a Mat-File'; [theFile, thePath] = uigetfile(theFilterSpec, thePrompt); if ~any(theFile), return, end if thePath(end) ~= filesep, thePath(end+1) = filesep; end theMatFile = [thePath theFile]; end if any(theROMSFile == '*') theFilterSpec = theROMSFile; thePrompt = 'Save As ROMS File'; [theFile, thePath] = uiputfile(theFilterSpec, thePrompt); if ~any(theFile), return, end if thePath(end) ~= filesep, thePath(end+1) = filesep; end theROMSFile = [thePath theFile]; end if isequal(theMatFile, theROMSFile) disp([' ## Must not select same file for input and output.']) return end % Load the Mat-File. s = load(theMatFile); if isempty(s) disp([' ## Mat-File is empty.']) return end % We need to pay attention to the (i, j) coordinates % stored in the file. They are our only clue on the % actual orientation of the data. The "i" index % goes left-to-right in the grid-world, while % the "j" index runs bottom-to-top. % WetCDF on. if isMacintosh eval('wetcdf on') end % Open the ROMS File. nc = netcdf(theROMSFile, 'clobber'); if isempty(nc) disp([' ## Unable to open ROMS NetCDF output file.']) return end % Populate the ROMS File. %% Global attributes: disp(' ## Defining Global Attributes...') nc.type = ncchar('Gridpak file'); theGridTitle = 'ROMS Model Grid'; nc.gridid = theGridTitle; nc.history = ncchar(['Created by "' mfilename '" on ' datestr(now)]); nc.CPP_options = ncchar('DCOMPLEX, DBLEPREC, NCARG_32, PLOTS,'); name(nc.CPP_options, 'CPP-options') % Dimensions: % mapping [m, n] = size(s.rho.lon); % The xi direction (left-right): LP = n; % The rho dimension. L = LP-1; % The psi dimension. % The eta direction (up-down): MP = m; % The rho dimension. M = MP-1; % The psi dimension. disp(' ## Defining Dimensions...') nc('xi_psi') = L; nc('xi_rho') = LP; nc('xi_u') = L; nc('xi_v') = LP; nc('eta_psi') = M; nc('eta_rho') = MP; nc('eta_u') = MP; nc('eta_v') = M; nc('one') = 1; nc('two') = 2; nc('bath') = 0; %% (record dimension) %% Variables and attributes: disp(' ## Defining Variables and Attributes...') nc{'xl'} = ncdouble('one'); %% 1 element. nc{'xl'}.long_name = ncchar('domain length in the XI-direction'); nc{'xl'}.units = ncchar('meter'); nc{'el'} = ncdouble('one'); %% 1 element. nc{'el'}.long_name = ncchar('domain length in the ETA-direction'); nc{'el'}.units = ncchar('meter'); nc{'JPRJ'} = ncchar('two'); %% 2 elements. nc{'JPRJ'}.long_name = ncchar('Map projection type'); nc{'JPRJ'}.option_ME_ = ncchar('Mercator'); nc{'JPRJ'}.option_ST_ = ncchar('Stereographic'); nc{'JPRJ'}.option_LC_ = ncchar('Lambert conformal conic'); name(nc{'JPRJ'}.option_ME_, 'option(ME)') name(nc{'JPRJ'}.option_ST_, 'option(ST)') name(nc{'JPRJ'}.option_LC_, 'option(LC)') nc{'PLAT'} = ncfloat('two'); %% 2 elements. nc{'PLAT'}.long_name = ncchar('Reference latitude(s) for map projection'); nc{'PLAT'}.units = ncchar('degree_north'); nc{'PLONG'} = ncfloat('one'); %% 1 element. nc{'PLONG'}.long_name = ncchar('Reference longitude for map projection'); nc{'PLONG'}.units = ncchar('degree_east'); nc{'ROTA'} = ncfloat('one'); %% 1 element. nc{'ROTA'}.long_name = ncchar('Rotation angle for map projection'); nc{'ROTA'}.units = ncchar('degree'); nc{'JLTS'} = ncchar('two'); %% 2 elements. nc{'JLTS'}.long_name = ncchar('How limits of map are chosen'); nc{'JLTS'}.option_CO_ = ncchar('P1, .. P4 define two opposite corners '); nc{'JLTS'}.option_MA_ = ncchar('Maximum (whole world)'); nc{'JLTS'}.option_AN_ = ncchar('Angles - P1..P4 define angles to edge of domain'); nc{'JLTS'}.option_LI_ = ncchar('Limits - P1..P4 define limits in u,v space'); name(nc{'JLTS'}.option_CO_, 'option(CO)') name(nc{'JLTS'}.option_MA_, 'option(MA)') name(nc{'JLTS'}.option_AN_, 'option(AN)') name(nc{'JLTS'}.option_LI_, 'option(LI)') nc{'P1'} = ncfloat('one'); %% 1 element. nc{'P1'}.long_name = ncchar('Map limit parameter number 1'); nc{'P2'} = ncfloat('one'); %% 1 element. nc{'P2'}.long_name = ncchar('Map limit parameter number 2'); nc{'P3'} = ncfloat('one'); %% 1 element. nc{'P3'}.long_name = ncchar('Map limit parameter number 3'); nc{'P4'} = ncfloat('one'); %% 1 element. nc{'P4'}.long_name = ncchar('Map limit parameter number 4'); nc{'XOFF'} = ncfloat('one'); %% 1 element. nc{'XOFF'}.long_name = ncchar('Offset in x direction'); nc{'XOFF'}.units = ncchar('meter'); nc{'YOFF'} = ncfloat('one'); %% 1 element. nc{'YOFF'}.long_name = ncchar('Offset in y direction'); nc{'YOFF'}.units = ncchar('meter'); nc{'depthmin'} = ncshort('one'); %% 1 element. nc{'depthmin'}.long_name = ncchar('Shallow bathymetry clipping depth'); nc{'depthmin'}.units = ncchar('meter'); nc{'depthmax'} = ncshort('one'); %% 1 element. nc{'depthmax'}.long_name = ncchar('Deep bathymetry clipping depth'); nc{'depthmax'}.units = ncchar('meter'); nc{'spherical'} = ncchar('one'); %% 1 element. nc{'spherical'}.long_name = ncchar('Grid type logical switch'); nc{'spherical'}.option_T_ = ncchar('spherical'); nc{'spherical'}.option_F_ = ncchar('Cartesian'); name(nc{'spherical'}.option_T_, 'option(T)') name(nc{'spherical'}.option_F_, 'option(F)') nc{'hraw'} = ncdouble('bath', 'eta_rho', 'xi_rho'); %% 0 elements. nc{'hraw'}.long_name = ncchar('Working bathymetry at RHO-points'); nc{'hraw'}.units = ncchar('meter'); nc{'hraw'}.field = ncchar('bath, scalar'); nc{'h'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'h'}.long_name = ncchar('Final bathymetry at RHO-points'); nc{'h'}.units = ncchar('meter'); nc{'h'}.field = ncchar('bath, scalar'); nc{'f'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'f'}.long_name = ncchar('Coriolis parameter at RHO-points'); nc{'f'}.units = ncchar('second-1'); nc{'f'}.field = ncchar('Coriolis, scalar'); nc{'pm'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'pm'}.long_name = ncchar('curvilinear coordinate metric in XI'); nc{'pm'}.units = ncchar('meter-1'); nc{'pm'}.field = ncchar('pm, scalar'); nc{'pn'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'pn'}.long_name = ncchar('curvilinear coordinate metric in ETA'); nc{'pn'}.units = ncchar('meter-1'); nc{'pn'}.field = ncchar('pn, scalar'); nc{'dndx'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'dndx'}.long_name = ncchar('xi derivative of inverse metric factor pn'); nc{'dndx'}.units = ncchar('meter'); nc{'dndx'}.field = ncchar('dndx, scalar'); nc{'dmde'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'dmde'}.long_name = ncchar('eta derivative of inverse metric factor pm'); nc{'dmde'}.units = ncchar('meter'); nc{'dmde'}.field = ncchar('dmde, scalar'); nc{'x_rho'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'x_rho'}.long_name = ncchar('x location of RHO-points'); nc{'x_rho'}.units = ncchar('meter'); nc{'y_rho'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'y_rho'}.long_name = ncchar('y location of RHO-points'); nc{'y_rho'}.units = ncchar('meter'); nc{'x_psi'} = ncdouble('eta_psi', 'xi_psi'); %% 16641 elements. nc{'x_psi'}.long_name = ncchar('x location of PSI-points'); nc{'x_psi'}.units = ncchar('meter'); nc{'y_psi'} = ncdouble('eta_psi', 'xi_psi'); %% 16641 elements. nc{'y_psi'}.long_name = ncchar('y location of PSI-points'); nc{'y_psi'}.units = ncchar('meter'); nc{'x_u'} = ncdouble('eta_u', 'xi_u'); %% 16770 elements. nc{'x_u'}.long_name = ncchar('x location of U-points'); nc{'x_u'}.units = ncchar('meter'); nc{'y_u'} = ncdouble('eta_u', 'xi_u'); %% 16770 elements. nc{'y_u'}.long_name = ncchar('y location of U-points'); nc{'y_u'}.units = ncchar('meter'); nc{'x_v'} = ncdouble('eta_v', 'xi_v'); %% 16770 elements. nc{'x_v'}.long_name = ncchar('x location of V-points'); nc{'x_v'}.units = ncchar('meter'); nc{'y_v'} = ncdouble('eta_v', 'xi_v'); %% 16770 elements. nc{'y_v'}.long_name = ncchar('y location of V-points'); nc{'y_v'}.units = ncchar('meter'); nc{'lat_rho'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'lat_rho'}.long_name = ncchar('latitude of RHO-points'); nc{'lat_rho'}.units = ncchar('degree_north'); nc{'lon_rho'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'lon_rho'}.long_name = ncchar('longitude of RHO-points'); nc{'lon_rho'}.units = ncchar('degree_east'); nc{'lat_psi'} = ncdouble('eta_psi', 'xi_psi'); %% 16641 elements. nc{'lat_psi'}.long_name = ncchar('latitude of PSI-points'); nc{'lat_psi'}.units = ncchar('degree_north'); nc{'lon_psi'} = ncdouble('eta_psi', 'xi_psi'); %% 16641 elements. nc{'lon_psi'}.long_name = ncchar('longitude of PSI-points'); nc{'lon_psi'}.units = ncchar('degree_east'); nc{'lat_u'} = ncdouble('eta_u', 'xi_u'); %% 16770 elements. nc{'lat_u'}.long_name = ncchar('latitude of U-points'); nc{'lat_u'}.units = ncchar('degree_north'); nc{'lon_u'} = ncdouble('eta_u', 'xi_u'); %% 16770 elements. nc{'lon_u'}.long_name = ncchar('longitude of U-points'); nc{'lon_u'}.units = ncchar('degree_east'); nc{'lat_v'} = ncdouble('eta_v', 'xi_v'); %% 16770 elements. nc{'lat_v'}.long_name = ncchar('latitude of V-points'); nc{'lat_v'}.units = ncchar('degree_north'); nc{'lon_v'} = ncdouble('eta_v', 'xi_v'); %% 16770 elements. nc{'lon_v'}.long_name = ncchar('longitude of V-points'); nc{'lon_v'}.units = ncchar('degree_east'); nc{'mask_rho'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'mask_rho'}.long_name = ncchar('mask on RHO-points'); nc{'mask_rho'}.option_0_ = ncchar('land'); nc{'mask_rho'}.option_1_ = ncchar('water'); name(nc{'mask_rho'}.option_0_, 'option(0)') name(nc{'mask_rho'}.option_1_, 'option(1)') nc{'mask_u'} = ncdouble('eta_u', 'xi_u'); %% 16770 elements. nc{'mask_u'}.long_name = ncchar('mask on U-points'); nc{'mask_u'}.option_0_ = ncchar('land'); nc{'mask_u'}.option_1_ = ncchar('water'); name(nc{'mask_u'}.option_0_, 'option(0)') name(nc{'mask_u'}.option_1_, 'option(1)') % nc{'mask_u'}.FillValue_ = ncdouble(1); nc{'mask_v'} = ncdouble('eta_v', 'xi_v'); %% 16770 elements. nc{'mask_v'}.long_name = ncchar('mask on V-points'); nc{'mask_v'}.option_0_ = ncchar('land'); nc{'mask_v'}.option_1_ = ncchar('water'); name(nc{'mask_v'}.option_0_, 'option(0)') name(nc{'mask_v'}.option_1_, 'option(1)') % nc{'mask_v'}.FillValue_ = ncdouble(1); nc{'mask_psi'} = ncdouble('eta_psi', 'xi_psi'); %% 16641 elements. nc{'mask_psi'}.long_name = ncchar('mask on PSI-points'); nc{'mask_psi'}.option_0_ = ncchar('land'); nc{'mask_psi'}.option_1_ = ncchar('water'); name(nc{'mask_psi'}.option_0_, 'option(0)') name(nc{'mask_psi'}.option_1_, 'option(1)') % nc{'mask_psi'}.FillValue_ = ncdouble(1); % Now, what about depths: "h" and "hraw". <== DEPTHS. nc{'angle'} = ncdouble('eta_rho', 'xi_rho'); %% 16900 elements. nc{'angle'}.long_name = ncchar('angle between xi axis and east'); nc{'angle'}.units = ncchar('radians'); % Variables. % Fill the variables with data. disp(' ## Filling Variables...') if ~isfield(s, 'projection') | isempty(s.projection) s.projection = 'Mercator'; end projection = s.projection; switch lower(projection) case 'mercator' theProjection = 'ME'; case 'stereographic' theProjection = 'ST'; case 'lambert conformal conic' theProjection = 'LC'; otherwise theProjection = '??'; end nc{'JPRJ'}(1:2) = theProjection; nc{'spherical'}(:) = 'T'; % T or F -- uppercase okay? % Set projection parameters %stdlt1o = 60.00 %stdlt2o = 30.00 %stdlono = 80.00 %m_proj('Lambert Conformal Conic','clon',stdlono,'para',[stdlt1o stdlt2o]); m_proj(projection); % Geographic coordinates. lon=s.rho.lon; lat=s.rho.lat; [x,y] = m_ll2xy(lon,lat); radius=6371229.0; % earth radius used by COAMPS x=x*radius; y=y*radius; grid_x = interp2(x, 1); grid_y = interp2(y, 1); % interpolate Cartesian coordinates to get Geographic Coordinates at % non-grid cell centers [geogrid_lon, geogrid_lat] = m_xy2ll(grid_x/radius, grid_y/radius); % Degrees. xl = max(grid_x(:)) - min(grid_x(:)); el = max(grid_y(:)) - min(grid_y(:)); nc{'xl'}(:) = xl; nc{'el'}(:) = el; f = sw_f(lat); nc{'f'}(:) = f; % Handy indices. [m2, n2] = size(grid_x); i_rho = 1:2:n2; j_rho = 1:2:m2; i_psi = 2:2:n2; j_psi = 2:2:m2; i_u = 2:2:n2; j_u = 1:2:m2; i_v = 1:2:n2; j_v = 2:2:m2; % Locations. nc{'x_rho'}(:) = x; nc{'y_rho'}(:) = y; nc{'x_psi'}(:) = grid_x(j_psi, i_psi); nc{'y_psi'}(:) = grid_y(j_psi, i_psi); nc{'x_u'}(:) = grid_x(j_u, i_u); nc{'y_u'}(:) = grid_y(j_u, i_u); nc{'x_v'}(:) = grid_x(j_v, i_v); nc{'y_v'}(:) = grid_y(j_v, i_v); nc{'lon_rho'}(:) = geogrid_lon(j_rho, i_rho); nc{'lat_rho'}(:) = geogrid_lat(j_rho, i_rho); nc{'lon_psi'}(:) = geogrid_lon(j_psi, i_psi); nc{'lat_psi'}(:) = geogrid_lat(j_psi, i_psi); lon_u = geogrid_lon(j_u, i_u); lat_u = geogrid_lat(j_u, i_u); nc{'lon_u'}(:) = lon_u; nc{'lat_u'}(:) = lat_u; lon_v = geogrid_lon(j_v, i_v); lat_v = geogrid_lat(j_v, i_v); nc{'lon_v'}(:) = lon_v; nc{'lat_v'}(:) = lat_v; % Metric factors: use ones if absent. if ~isfield(s.rho, 'dx') | isempty(s.rho.dx) s.rho.dx = ones(size(nc{'pm'})); end if ~isfield(s.rho, 'dy') | isempty(s.rho.dy) s.rho.dy = ones(size(nc{'pn'})); end % dx = s.rho.dx; % NO! This is wrong. Should calculate % dy = s.rho.dy; % dx and dy on spherical earth, not in projected coords % use sw_dist to compute metric distances between lon/lon points lat_u=lat_u.'; lon_u=lon_u.'; [dx,ang]=sw_dist(lat_u(:),lon_u(:),'km'); dx=[dx(:); dx(end)]*1000; % km==> m dx=reshape(dx,n-1,m); dx=dx.'; dx=[dx(:,1) dx(:,(1:(end-1))) dx(:,end-1)]; ang=[ang(:); ang(end)]; ang=reshape(ang,n-1,m); ang=ang.'; ang=[ang(:,1) ang(:,(1:(end-1))) ang(:,end-1)]; dy=sw_dist(lat_v(:),lon_v(:),'km'); dy=[dy(:); dy(end)]*1000; % km ==> m dy=reshape(dy,m-1,n); dy=[dy(1,:); dy((1:(end-1)),:); dy(end-1,:)]; dx(dx == 0) = NaN; % Shouldn't be any zeros here. dy(dy == 0) = NaN; pm = 1 ./ dx; % Note reciprocals. pn = 1 ./ dy; % Does ROMS tolerate NaNs and/or Infs? % Do we have to substitute a NetCDF "_FillValue"? nc{'pm'}(:) = pm; nc{'pn'}(:) = pn; dmde = zeros(size(pm)); dndx = zeros(size(pn)); dmde(2:end-1, :) = 0.5*(1./pm(3:end, :) - 1./pm(1:end-2, :)); dndx(:, 2:end-1) = 0.5*(1./pn(:, 3:end) - 1./pn(:, 1:end-2)); nc{'dmde'}(:) = dmde; nc{'dndx'}(:) = dndx; % Depths at RHO points. bathymetry = s.rho.depth; mask = isnan(bathymetry) | (bathymetry == -99999); % ECOM land = -99999. bathymetry(mask) = min(bathymetry(:)); % set bathy under land to min depth water = 1 - mask; % ROMS requires water = 1, land= 0. if ~isempty(bathymetry) nc{'h'}(:) = bathymetry; % ROMS depths are positive. end % Angles at RHO points nc{'angle'}(:) = ang*pi/180; % Radians. % The u, v, and psi masks rely directly on % the mask of the surrounding rho points. nc{'mask_rho'}(:) = water; nc{'mask_u'}(:) = water(:, 1:end-1) & water(:, 2:end); nc{'mask_v'}(:) = water(1:end-1, :) & water(2:end, :); nc{'mask_psi'}(:) = water(1:end-1, 1:end-1) &... water(1:end-1, 2:end) & ... water(2:end, 1:end-1) & ... water(2:end, 2:end); % Close the ROMS File. if ~isempty(close(nc)) disp(' ## Unable too close the ROMS output file.') end % WetCDF off. if isMacintosh eval('wetcdf off') end % ---------- romsnan ---------- % function r = romsnan % romsnan -- NaN value prefered by ROMS. % romsnan (no argument) returns the value prefered % by ROMS in place of NaN. *** I believe it is % the NetCDF _FillValue for doubles. (CRD) f = ncfillvalues; r = f.ncdouble;