% geoadjPEmovie; adjustment under gravity and rotation in 1-D % % % A 1-D numerical ocean model of a single % shallow layer of rotating, incompressible fluid. % Useful for showing the numerical version of geostrophic % adjustment. The numerical methods used here are just adequate % but not great: an A-grid (velocity and height at the % same grid points) and upwind differencing on advection % terms to preserve a shock. Time-stepping is by the % leap-frog/trapezoidal time method. This may be about the % simplest PE model that works reasonably well for non-linear % cases, i.e., you can recognize the major features of the % solution, including a fairly reasonable shock (or bore) in % the case of low latitude adjustment. For zero rotation, see % instead the script bore.m, which differs mainly in the form % of non-dimensionalization. % % Jim Price, January, 2001. June 2004 to refine the plotting. clear close all format compact set(0,'DefaultLineLineWidth',1.5) set(0,'DefaultTextFontSize',13) set(0,'DefaultAxesLineWidth',1.4) set(0,'DefaultAxesFontSize',13) % display the text string str2 str2(1) = {'geoadjPE:' }; str2(2) = {' '}; str2(3) = {'Geostrophic adjustment in one dimension. '}; str2(4) = {'Solved by a primitive equation numerical '}; str2(5) = {'model representing one (shallow) layer of'}; str2(6) = {'nominal thickness 100 m. There is a ridge that'}; str2(7) = {'has zero initial current. There are four'}; str2(8) = {'parameters that are entered from the keyboard:'} str2(9) = {' latitude, degrees, 2 -- 90, say,'}; str2(10)= {' width of the ridge, kilometers, 50 -- 500, '}; str2(11)= {' height of the ridge, meters, 1 -- 50'}; str2(12)= {' the time of integration in days, 1 -- 50'}; str2(13)= {' '}; str2(14) = {'This is public domain for educational purposes.'}; str2(15) = {' '}; str2(16) = {'Hit any key to continue.'}; str2(17) = {''}; str2(18) = {'Jim Price, January, 2001.'}; hf3 = figure(1); clf set(hf3,'Position',[200 200 500 500]) set(gca,'Visible','off') text(0, 0.50, str2,'FontSize',12,'HorizontalAlignment','left') pause close(1) H = 500.; % the nominal layer thickness, m g = 9.8; delr = 2/1030; % fractional density change % ICtype = menu('What sort of IC?', ... % 'initial interface displacement', 'initial current') ICtype = 1; % hardwire this to avoid the many hangups caused by menu lat = input(' Enter the latitude (degrees, 0, or 2 < lat < 90) '); f = 2*7.29e-5*sin(lat*pi/180); IP = 2*pi/(f + 1.e-10); % set a flag to indicate if rotation is 'on' rota = 1; if abs(lat) <= 0.1 rota = 0; end fwidth = input(' Enter the width of the ridge (km, 50 - 500) '); delH = input(' Enter the amplitude of the surface bump (m, 1 - 50) '); c = sqrt(delr*g*H) % + delH)); % shallow water phase speed disp(' the non-disersive phase speed, m/s is ') c Rd = 1.e10; if rota == 1 Rd = c/(f + 1.e-10); % Rossby radius of deformation disp(' the Rossby radius of deformation, km is ') Rdkm = Rd/1000. end Ugeo = g*delr*delH/(f*Rd); Usc = Ugeo; % this U scale is good for rotation if rota == 0 % this U scale is OK if no rotation Usc = (delH/H)*c; end disp(' the velocity scale, m/s is ') Usc % define the grid dx = Rd/8; % grid interval, units of Rd L = 20.*Rd; % domain half-width, Rd (the larger, the better) % change this to a fixed interval (not scaled with Rd) so will work with f % = 0. if rota == 0 dx = 15.e3; L = 2000.e3; end % dx = 5.e3; % L = 100*dx; xu = -L:dx:L; % grid points for u,v,h ndays = input(' Enter the time to integrate (days, 1 - 20) '); CFL = 0.20; % the CFL number, used to set dt dt = CFL*dx/c; % time step, seconds nstep = round(ndays*8.64e4/dt); % number of time steps to take K = 1.e2; % horizontal diffusion (keep this small) disp(' the diffusion length scale, km is ') Kd = sqrt(K*ndays*8.64e4)/1000. udecay = (1./(5.*8.64e4)); % Rayleigh drag (not used at present) udecay = 0.; nonlinu = 1.; % set these both to 0 to get a linear model nonlinh = 1.; % (for advection of momentum and thickness) [m nu] = size(xu); xh = ones(1,nu-1); xh = cumsum(xh)*dx; nh = nu - 1; mov = 1; if mov == 1 M = moviein(round(nstep/20)); end % pre-allocate some arrays u = zeros(1,nu); unew = u; uold = u; uold2 = u; ut = u; v = u; vnew = u; vold = u; vold2 = u; vt = u; h = u; ht = u; hnew = u; holda = u; hold2 = u; na = 0; ha = u; ua = u; va = u; % pre-allocate some arrays for storage of diagncostics uc = zeros(1,nstep); hc = uc; vc = uc; he = uc; ue = uc; ve = vc; ts = uc; ns9 = floor(nstep/20); % for data that are decimated in time ts9 = zeros(1, ns9); hs = zeros(nu, ns9); vs = hs; ns = 0; % a counter used to keep track of how many stored % Specify the IC, first on h, the interface displacement above the background. Hscale = 20.e3; % scale over which h changes, 10-30 km is OK xl = fwidth*1000.; % convert xl to m for m = 1:nh xp = xh(m) - L; if abs(xp) <= xl; h(m) = delH; else h(m) = 0; end % if xp < 0; h(m) = delH*(0.5 + 0.5*tanh((xp + xl)/Hscale)); end; % if xp > 0; h(m) = delH*(0.5 + 0.5*tanh(-(xp - xl)/Hscale)); end; end length(h) % smooth just a little for m=2:nh a4(m) = 0.25*(h(m-1) + h(m+1)) + 0.5*h(m); end a4(1) = h(1); a4(nh+1) = h(nh+1); h = a4; length(h) % or switch this to the current if you chose if ICtype == 2 v = h*Usc/delH; h = 0; end % initialize time levels holda = h; hold2 = h; h0 = h; vold = v; vold2 = v; v0 = v; % store the initial h and PV hi = h; PVi = f./(H + h); for m = 1:nstep % time step this baby; use a leap-frog method with an occasional % trapezoidal correction to supress time splitting if m == 1 % actually, the first time step is Euler forward du = 0.5*(diff([0 u]) + diff([u 0])); dv = 0.5*(diff([0 v]) + diff([v 0])); dh = 0.5*(diff([0 h]) + diff([h 0])); du(1) = 0.; du(nu) = 0.; dv(1) = 0.; dv(nu) = 0.; dh(1) = 0.; dh(nu) = 0.; ut = f*v - (delr*g*dh/dx + udecay*u + nonlinu*u.*du/dx); vt = -(f*u + udecay*v + nonlinu*u.*dv/dx); ht = -(H*du/dx + nonlinh*h.*du/dx + nonlinh*u.*dh/dx); uold = u; vold = v; holda = h; u = uold + dt*ut; v = vold + dt*vt; h = holda + dt*ht; else trap = 100; mtype = mod(m,trap); % decide whether to do a trapezoidal step (20 - 100) if mtype >= 1 % come here for a simple leap frog du = 0.5*(diff([0 u]) + diff([u 0])); dv = 0.5*(diff([0 v]) + diff([v 0])); dh = 0.5*(diff([0 h]) + diff([h 0])); du(1) = 0.; du(nu) = 0.; dv(1) = 0.; dv(nu) = 0.; dh(1) = 0.; dh(nu) = 0.; % velocity and derivatives needed for an upwind difference ul = 0.5*(u + abs(u)); ur = 0.5*(u - abs(u)); dhl = diff([0 h]); dhl(1) = 0.; dhl(nu) = 0.; dhr = diff([h 0]); dhr(1) = 0.; dhr(nu) = 0.; dul = diff([0 u]); dul(1) = 0.; dul(nu) = 0.; dur = diff([u 0]); dur(1) = 0.; dur(nu) = 0.; dvl = diff([0 v]); dvl(1) = 0.; dvl(nu) = 0.; dvr = diff([v 0]); dvr(1) = 0.; dvr(nu) = 0.; du2 = [0 diff(u,2) 0]/dx^2; dv2 = [0 diff(v,2) 0]/dx^2; dh2 = [0 diff(h,2) 0]/dx^2; ut = f*v + K*du2 - (delr*g*dh/dx + udecay*u + ... nonlinu*(ur.*dur/dx + ul.*dul/dx)); vt = K*dv2 - (f*u + udecay*v + ... nonlinu*(ur.*dvr/dx + ul.*dvl/dx)); ht = K*dh2 - (H*du/dx + nonlinh*h.*du/dx + ... nonlinh*(ur.*dhr/dx + ul.*dhl/dx)); uold2 = uold; vold2 = vold; hold2 = holda; uold = u; vold = v; holda = h; u = uold2 + 2*dt*ut; v = vold2 + 2*dt*vt; h = hold2 + 2*dt*ht; else % do a leap-frog/trapezoidal time step du = 0.5*(diff([0 u]) + diff([u 0])); dv = 0.5*(diff([0 v]) + diff([v 0])); dh = 0.5*(diff([0 h]) + diff([h 0])); du(1) = 0.; du(nu) = 0.; dv(1) = 0.; dv(nu) = 0.; dh(1) = 0.; dh(nu) = 0.; % velocity and derivatives needed for an upwind difference ul = 0.5*(u + abs(u)); ur = 0.5*(u - abs(u)); dhl = diff([0 h]); dhl(1) = 0.; dhl(nu) = 0.; dhr = diff([h 0]); dhr(1) = 0.; dhr(nu) = 0.; dul = diff([0 u]); dul(1) = 0.; dul(nu) = 0.; dur = diff([u 0]); dur(1) = 0.; dur(nu) = 0.; dvl = diff([0 v]); dvl(1) = 0.; dvl(nu) = 0.; dvr = diff([v 0]); dvr(1) = 0.; dvr(nu) = 0.; du2 = [0 diff(u,2) 0]/dx^2; dv2 = [0 diff(v,2) 0]/dx^2; dh2 = [0 diff(h,2) 0]/dx^2; ut = f*v + K*du2 - (delr*g*dh/dx + udecay*u + ... nonlinu*(ur.*dur/dx + ul.*dul/dx)); vt = K*dv2 - (f*u + udecay*v + ... nonlinu*(ur.*dvr/dx + ul.*dvl/dx)); ht = K*dh2 - (H*du/dx + nonlinh*h.*du/dx + ... nonlinh*(ur.*dhr/dx + ul.*dhl/dx)); u1 = u + 0.5*dt*ut; v1 = v + 0.5*dt*vt; h1 = h + 0.5*dt*ht; du = 0.5*(diff([0 u1]) + diff([u1 0])); dv = 0.5*(diff([0 v1]) + diff([v1 0])); dh = 0.5*(diff([0 h1]) + diff([h1 0])); du(1) = 0.; du(nu) = 0.; dv(1) = 0.; dv(nu) = 0.; dh(1) = 0.; dh(nu) = 0.; % velocity and derivatives needed for an upwind difference ul = 0.5*(u1 + abs(u1)); ur = 0.5*(u1 - abs(u1)); dhl = diff([0 h1]); dhl(1) = 0.; dhl(nu) = 0.; dhr = diff([h1 0]); dhr(1) = 0.; dhr(nu) = 0.; dul = diff([0 u1]); dul(1) = 0.; dul(nu) = 0.; dur = diff([u1 0]); dur(1) = 0.; dur(nu) = 0.; dvl = diff([0 v1]); dvl(1) = 0.; dvl(nu) = 0.; dvr = diff([v1 0]); dvr(1) = 0.; dvr(nu) = 0.; du2 = [0 diff(u1,2) 0]/dx^2; dv2 = [0 diff(v1,2) 0]/dx^2; dh2 = [0 diff(h1,2) 0]/dx^2; ut = f*v + K*du2 - (delr*g*dh/dx + udecay*u + ... nonlinu*(ur.*dur/dx + ul.*dul/dx)); vt = K*dv2 - (f*u + udecay*v + ... nonlinu*(ur.*dvr/dx + ul.*dvl/dx)); ht = K*dh2 - (H*du/dx + nonlinh*h.*du/dx + ... nonlinh*(ur.*dhr/dx + ul.*dhl/dx)); u = uold + dt*ut; v = vold + dt*vt; h = holda + dt*ht; uold = u; vold = v; holda = h; ut = 0.*ut; vt = 0.*vt; ht = 0.*ht; end % if on mtype end % if on m ==1 % Apply a radiation BC to the grid ends. This is only moderately % succesful because the phase speed changes in time. bcfactor = 1.5; % an ad hoc 'correction' for the radiation BC rbc = bcfactor*c*dt/dx; u(1) = u(1) + rbc*(u(2) - u(1)); v(1) = v(1) + rbc*(v(2) - v(1)); h(1) = h(1) + rbc*(h(2) - h(1)); u(nu) = u(nu) + rbc*(u(nu-1) - u(nu)); v(nu) = v(nu) + rbc*(v(nu-1) - v(nu)); h(nu) = h(nu) + rbc*(h(nu-1) - h(nu)); % store some data for plotting later ts(m) = m*dt/8.64e4; % the time (days) np = length(u); nf = np - round(xl/dx); hf(m) = h(nf); % far field h,u,v uf(m) = u(nf); vf(m) = v(nf); ne = round(np/2 + 500.e3/dx); he(m) = h(ne); % h,u,v at a point near the edge of the front ue(m) = u(ne); ve(m) = v(ne); na = na + 1; % sum for averages ha = ha + h; ua = ua + u; va = va + v; % evaluate the energy balance ke(m) = sum(0.5*(H + h).*(u.^2 + v.^2)); pe(m) = sum(0.5*g*delr*(h.^2)); % some diagnostic data are decimated in time if mod(m,20) == 1 % save the entire grid occasionally ns = ns+1; hs(:,ns) = h(:); vs(:,ns) = sqrt(v(:).^2 + u(:).^2); ts9(ns) = ts(m); % make a movie frame, occasionally if mov == 1 hf1 = figure(1); clf reset set(gcf, 'position', [250 250 850 400]) if rota == 1 axis([-15 15 -0.5 3.5]) else axis([-900 900 -0.5 3.5]) end vv = axis; xyscale = (vv(2) - vv(1))/(vv(4) - vv(3)); for q=1:2:nu hold on % xa = xu(q)/1000.; if rota == 1 xa = xu(q)/Rd; else xa = xu(q)/1.e3; end ya = 2.5; % xb = xa + (2*L/(1000*4))*u(q)/Usc; xb = xa + 7.5*uold(q)/Usc; % this 5 depends upon the axis set below yb = ya + v(q)/Usc; if rota == 0 % switch the components if there is no rotation xb = xa + 7.5*vold(q)/Usc; yb = ya + uold(q)/Usc; end plot([xa xb], [ya yb], 'LineWidth', 1.0) end % axis([-L/1000 L/1000 -0.5 3.5]) % may need to change these for each case if rota == 0 title(['gravitational adjustment, latitude = ', num2str(lat,2)]) else title(['geostrophic adjustment, latitude = ', num2str(lat,2)]) end hold on % plot(xu/1000., h/delH); % fill([xu(1)/1000. xu/1000. max(xu)/1000.], [-9 h/delH -9], [0.6 0.6 0.8]); if rota == 1 plot(xu/Rd, holda/delH); fill([xu(1)/Rd xu/Rd max(xu)/Rd], [-9 holda/delH -9], [0.6 0.6 0.8]); else plot(xu/1.e3, holda/delH); fill([xu(1)/1.e3 xu/1.e3 max(xu)/1.e3], [-9 holda/delH -9], [0.6 0.6 0.8]); end ylabel('\eta/\eta_o and U/(C \eta_o/H)') if rota == 1 ylabel('\eta/\eta_o and (U, V)/(C \eta_o/H)') end if rota == 1 xlabel('X/Rd') else xlabel('X, km') end timed = (floor(10*m*dt/8.64e4))/10.; if rota == 1 text(5., 1.5, ['time = ', num2str(timed), ' days']) tIP = timed*8.64e4/IP; timeIP = (floor(10*tIP))/10.; text(5., 1.2, [' = ', num2str(timeIP), ' IP']) else text(250., 1.5, ['time = ', num2str(timed), ' days']) end % text(15., 1.3, ['latitude = ', num2str(lat)]) M(:,ns) = getframe(gcf); end % if on movie or not end % if on plot this time step ot not end % end of the time stepping loop % compute time averages ha = ha/na; ua = ua/na; va = va/na; % plot some things figure(2) clf reset tip = ts; subplot(2,1,1) plot(ts, uf/Usc, 'b', ts, vf/Usc, '--r') ylabel('U far/(C \eta_o/H)') legend('U','V') title('Currents at the far field and edge') subplot(2,1,2) plot(ts, ue/Usc, 'b', ts, ve/Usc, '--r') xlabel('time, days'); ylabel('Uedge/(C \eta_o/H)') legend('U','V') figure(3) clf reset mesh(ts9, xu/1000., hs/delH) xlabel('time, days') ylabel('distance, km') zlabel('\eta/\eta_o') axis([0 ndays -L/1000 L/1000 -0.5 1.5]) title('Geostrophic/Gravitational adjustment') view([-70 60]) % add the speed c (cool) tx = 0:0.1:5; cx = tx*8.64e4*c; zx = 0.55*ones(1,length(tx)); hold on plot3(tx, cx/1000., zx, 'r', tx, -cx/1000., zx, 'r') if rota == 1 % compute the final PV dv = 0.5*(diff([0 va]) + diff([va 0])); dv(1) = 0.; dv(nu) = 0.; rvort = dv/dx; PVf = (f + rvort)./(H + h); PVflin = (f + 0*rvort)./(H + h); figure(4) clf reset subplot(2,1,1) plot(xu/1000., hi/delH, 'g', xu/1000., h/delH,'r'); %axis([-10 10 -30 30]); ylabel('\eta/\eta_o') legend('initial', 'final') title('Initial and final thickness and PV') subplot(2,1,2) plot(xu/1000., PVi,'g', xu/1000., PVf, 'r', xu/1000., PVflin, 'b'); %axis([-10 10 2.e-7 3.e-7]); xlabel('distance, km') ylabel('Potential Vorticity') legend('initial', 'final','final, linear') figure(5) clf reset subplot(2,1,1) plot(xu/1000., ua/Usc, xu/1000., va/Usc, '--r') xlabel('distance, km') ylabel('U, V avg speeds/(C \eta_o/H)') legend('Uavg', 'Vavg') title('Time average currents, etc.') subplot(2,1,2) plot(xu/1000., rvort/f) xlabel('distance, km') ylabel('rel vorticity/f') end figure(8) clf reset Esc = g*delr*delH^2*2*xl/dx; plot(ts, ke/Esc, ts, pe/Esc, ts, (ke+pe)/Esc) legend('KE', 'PE', 'KE+PE') xlabel('time, days') ylabel('energy/PE(t=0)') title('Energy budget') % shuffle the figures if rota == 1 figure(5) figure(4) end figure(3) figure(2) % imov = input(' Shall we save an mpeg movie file? (1 = yes)') % if imov == 1 % if exist('geoadjmovie.m') == 1; delete 'geoadjmovie.mpg'; end % end