Home > fvcom_postproc > show_max_CFL.m

show_max_CFL

PURPOSE ^

SHOW_MAX-CFL Function to find the max CFL encountered in each mesh element during an FVCOM model

SYNOPSIS ^

function [ maxCFLs, fighandle ] = show_max_CFL(grdFile, depFile, ncFile, extTS, coordtype, fig_flag)

DESCRIPTION ^

SHOW_MAX-CFL Function to find the max CFL encountered in each mesh element during an FVCOM model
run. NB need enough RAM to load all u, v and zeta data from the given
ncFile.
   Inputs: grdFile, depFile: casename_grd.dat and casename_dep.dat files
                              from an FVCOM model
           ncFile : Output from FVCOM. Must include u, v, and zeta.
           extTS : External timestep at which to calculate CFL, in
                   seconds.
           coordtype: 'lonlat' or 'cartesian'. If cartesian, coordinates
                  should be in metres. If lonlat, they should be in
                  degrees.
           fig_flag: Should a figure be plotted? true or false.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function [ maxCFLs, fighandle ] = show_max_CFL(grdFile, depFile, ncFile, extTS, coordtype, fig_flag)
0002 %SHOW_MAX-CFL Function to find the max CFL encountered in each mesh element during an FVCOM model
0003 %run. NB need enough RAM to load all u, v and zeta data from the given
0004 %ncFile.
0005 %   Inputs: grdFile, depFile: casename_grd.dat and casename_dep.dat files
0006 %                              from an FVCOM model
0007 %           ncFile : Output from FVCOM. Must include u, v, and zeta.
0008 %           extTS : External timestep at which to calculate CFL, in
0009 %                   seconds.
0010 %           coordtype: 'lonlat' or 'cartesian'. If cartesian, coordinates
0011 %                  should be in metres. If lonlat, they should be in
0012 %                  degrees.
0013 %           fig_flag: Should a figure be plotted? true or false.
0014 %
0015 
0016 % The formulation of CFL used here is taken from the MIKE 3 manual
0017 
0018 % Simon Waldman (Marine Scotland Science / Heriot-Watt University), March 2018
0019 
0020 %% Check inputs
0021 
0022 assert( nargin == 6, 'Wrong number of arguments.');
0023 assert( exist(grdFile, 'file')==2, 'Cannot find file %s', grdFile );
0024 assert( exist(depFile, 'file')==2, 'Cannot find file %s', depFile );
0025 assert( exist(ncFile, 'file')==2, 'Cannot find file %s', ncFile );
0026 assert( ischar(coordtype), 'Coord type must be ''lonlat'' or ''cartesian''.' );
0027 assert( islogical( fig_flag ), 'fig_flag should be logical.' );
0028 
0029 switch coordtype
0030     case 'lonlat'
0031         lonlat=true;
0032     case 'latlon'
0033         lonlat=true;
0034     case 'cartesian'
0035         lonlat=false;
0036     otherwise
0037         error('Coord type must be ''lonlat'' or ''cartesian''.');
0038 end
0039 
0040 % does the ncFile have velocities & water levels?
0041 
0042 info = ncinfo(ncFile);
0043 assert( any(strcmp('u', {info.Variables.Name})) && any(strcmp('v', {info.Variables.Name})) && ...
0044     any(strcmp('zeta', {info.Variables.Name})), 'netCDF file must include the fields u, v and zeta.' );
0045 
0046 %% Do the stuff.
0047 
0048 disp('Reading mesh & bathymetry...');
0049 M = read_fvcom_mesh( grdFile ); %NB this function puts lon and lat in the x and y fields.
0050 M.h = read_fvcom_bath( depFile );
0051 % calculate element depths from mean of nodes
0052 M.hc = mean(M.h(M.tri),2);
0053 
0054 disp('Reading U velocities...');
0055 U = ncread(ncFile, 'u');
0056 disp('Reading V velocities...');
0057 V = ncread(ncFile, 'v');
0058 disp('Reading surface elevations...');
0059 Z = ncread(ncFile, 'zeta');
0060 
0061 g = 9.81;
0062 
0063 NumTS = size(U, 3);
0064 NumEl = size(U, 1);
0065 
0066 % Find water depths for each element at each TS. First calculate this for
0067 % nodes, then average them for elements.
0068 disp('Calculating depths...');
0069 NodeDepths = repmat(M.h, 1, NumTS) + Z; %repmat because Z has a time dimension, M.h doesn't.
0070 for n = 1:3
0071     tmp(:,:,n) = NodeDepths(M.tri(:,n), :);
0072 end
0073 ElDepths = mean(tmp, 3); %this has dimensions of element x TS.
0074 clear Z NodeDepths tmp; %save some memory
0075 
0076 % Find minimum characteristic length for each element. This is approximated by the
0077 % minimum edge length. It could be shorter with really long thin triangles,
0078 % but the 30 degree internal angle minimum for FVCOM means we shouldn't have those.
0079 
0080 disp('Calculating triangle sizes...');
0081 CharLen = nan(NumEl, 1);
0082 for e = 1:NumEl %for each element
0083     xv = M.x(M.tri(e,:)); %vertices.
0084     yv = M.y(M.tri(e,:));
0085     %close the triangle by copying the first vertex to the end
0086     xv(4) = xv(1);
0087     yv(4) = yv(1);
0088     %find the edge lengths
0089     if lonlat
0090         for a = 1:3
0091             edges(a) = haversine(yv(a), xv(a), yv(a+1), xv(a+1));
0092         end
0093     else
0094         edges = sqrt( diff(xv).^2 + diff(yv).^2 );
0095     end
0096     CharLen(e) = min(edges);
0097 end
0098 
0099 % For each element and TS, find the layer with the highest U and V magnitudes.
0100 % Technically doing this with each component rather than the vector magnitude
0101 % is wrong, but it'll usually be close and where wrong, it'll overestimate the CFL, so it's safe.
0102 maxU = squeeze(max(abs(U), [], 2));
0103 maxV = squeeze(max(abs(V), [], 2));
0104     
0105 % Find CFL for each element at each TS
0106 CFL = ( 2 .* sqrt( g .* ElDepths ) + maxU + maxV ) .* repmat( (extTS ./ CharLen), 1, NumTS );
0107 %This is based on equation 6.1 on pg 33 of the MIKE hydrodynamic module
0108 %manual (modified for using a single characteristic length rather than
0109 %deltaX/deltaY)
0110 
0111 % find the max over time for each element
0112 MaxCFL = max(CFL, [], 2);
0113 
0114 [val, I] = max(MaxCFL);
0115 fprintf('Max CFL reached with an external timestep of %.2f secs is approx. %.3f, in Element %i.\n', extTS, val, I);
0116 
0117 % find how long the timestep probably could go. We set the CFL to 0.8 and
0118 % apply the same formula backwards.
0119 TargetCFL = 0.8;
0120 MaxTSs = repmat( (TargetCFL .* CharLen), 1, NumTS ) ./ ( 2 .* sqrt( g .* ElDepths ) + maxU + maxV );
0121 %that's still per element per TS. We care about what the smallest is.
0122 OverallMaxTS = min(min(MaxTSs));
0123 
0124 fprintf('Max external timestep to reach CFL of %.1f with this mesh would be approx. %.2f seconds.\n', TargetCFL, OverallMaxTS );
0125 
0126 % Optionally, plot figure of this
0127 if fig_flag
0128     CFLfig = figure;
0129     p = patch();
0130     p.Vertices = [M.x M.y];
0131     p.Faces = M.tri;
0132     p.CData = MaxCFL;
0133     p.FaceColor = 'flat';
0134     %p.EdgeColor = [0 0 0];
0135     p.EdgeColor = 'none';
0136     p.EdgeAlpha = 0.1;
0137     p.LineWidth = 0.1;
0138     cb = colorbar;
0139     colormap(parula);
0140     caxis([0 max(MaxCFL)]);
0141     ylabel(cb, 'Max CFL encountered.');
0142     axis equal;
0143     
0144     % add to the plot markers for the worst n elements
0145     n = 10; %number of cells to highlight
0146     [ ~, WorstEls ] = sort(MaxCFL, 'descend');
0147     WorstElsX = mean(M.x(M.tri(WorstEls(1:n),:)), 2); %finding element centroid coords.
0148     WorstElsY = mean(M.y(M.tri(WorstEls(1:n),:)), 2);
0149     hold on;
0150     plot(WorstElsX, WorstElsY, 'or');
0151     fprintf('Red circles on plot show the %i mesh elements with the highest CFL.\n', n);
0152 end
0153 
0154 %return values
0155 maxCFLs = MaxCFL;
0156 fighandle = CFLfig;
0157 
0158 end
0159 
0160 
0161 
0162 function [distm]=haversine(lat1,lon1,lat2,lon2)
0163 % Haversine function to calculate first order distance measurement. Assumes
0164 % spherical Earth surface. Lifted from:
0165 %
0166 % http://www.mathworks.com/matlabcentral/fileexchange/27785
0167 
0168 % Could be done more accurately with Mapping Toolbox tools, but don't want
0169 % to require that, and we don't need amazing accuracy.
0170 
0171 lat1 = deg2rad(lat1);
0172 lat2 = deg2rad(lat2);
0173 lon1 = deg2rad(lon1);
0174 lon2 = deg2rad(lon2);
0175 R = 6371000;                    % Earth's mean radius in metres
0176 delta_lat = lat2 - lat1;        % difference in latitude
0177 delta_lon = lon2 - lon1;        % difference in longitude
0178 a = sin(delta_lat/2)^2 + cos(lat1) * cos(lat2) * ...
0179     sin(delta_lon/2)^2;
0180 c = 2 * atan2(sqrt(a), sqrt(1-a));
0181 distm = R * c;                     % distance in metres
0182 end

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