From 55b85b1d452bb38ef335762911e0b0cac5ae11f2 Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 14 May 2019 16:15:15 +0100 Subject: [PATCH 01/34] Added code necessary to run shallow water and barotropic vorticity codes to Isca. Downloaded necessary files from ftp://ftp.gfdl.noaa.gov/perm/GFDL_pubrelease/Spectral_Idealized_Atmospheric_Models/spectral_idealized_public_release.tar.gz on 14/05/19. --- src/atmos_spectral_barotropic/atmosphere.F90 | 246 +++++++ src/atmos_spectral_barotropic/atmosphere.html | 174 +++++ src/atmos_spectral_barotropic/barotropic.pdf | Bin 0 -> 80013 bytes .../barotropic_diagnostics.F90 | 152 +++++ .../barotropic_diagnostics.html | 152 +++++ .../barotropic_dynamics.F90 | 573 +++++++++++++++++ .../barotropic_dynamics.html | 371 +++++++++++ .../barotropic_physics.F90 | 173 +++++ .../barotropic_physics.html | 165 +++++ src/atmos_spectral_barotropic/stirring.F90 | 252 ++++++++ src/atmos_spectral_shallow/atmosphere.F90 | 279 ++++++++ src/atmos_spectral_shallow/atmosphere.html | 164 +++++ src/atmos_spectral_shallow/shallow.pdf | Bin 0 -> 62941 bytes .../shallow_diagnostics.F90 | 139 ++++ .../shallow_diagnostics.html | 150 +++++ .../shallow_dynamics.F90 | 607 ++++++++++++++++++ .../shallow_dynamics.html | 370 +++++++++++ .../shallow_physics.F90 | 232 +++++++ .../shallow_physics.html | 210 ++++++ 19 files changed, 4409 insertions(+) create mode 100644 src/atmos_spectral_barotropic/atmosphere.F90 create mode 100644 src/atmos_spectral_barotropic/atmosphere.html create mode 100644 src/atmos_spectral_barotropic/barotropic.pdf create mode 100644 src/atmos_spectral_barotropic/barotropic_diagnostics.F90 create mode 100644 src/atmos_spectral_barotropic/barotropic_diagnostics.html create mode 100644 src/atmos_spectral_barotropic/barotropic_dynamics.F90 create mode 100644 src/atmos_spectral_barotropic/barotropic_dynamics.html create mode 100644 src/atmos_spectral_barotropic/barotropic_physics.F90 create mode 100644 src/atmos_spectral_barotropic/barotropic_physics.html create mode 100644 src/atmos_spectral_barotropic/stirring.F90 create mode 100644 src/atmos_spectral_shallow/atmosphere.F90 create mode 100644 src/atmos_spectral_shallow/atmosphere.html create mode 100644 src/atmos_spectral_shallow/shallow.pdf create mode 100644 src/atmos_spectral_shallow/shallow_diagnostics.F90 create mode 100644 src/atmos_spectral_shallow/shallow_diagnostics.html create mode 100644 src/atmos_spectral_shallow/shallow_dynamics.F90 create mode 100644 src/atmos_spectral_shallow/shallow_dynamics.html create mode 100644 src/atmos_spectral_shallow/shallow_physics.F90 create mode 100644 src/atmos_spectral_shallow/shallow_physics.html diff --git a/src/atmos_spectral_barotropic/atmosphere.F90 b/src/atmos_spectral_barotropic/atmosphere.F90 new file mode 100644 index 000000000..40a01ebb1 --- /dev/null +++ b/src/atmos_spectral_barotropic/atmosphere.F90 @@ -0,0 +1,246 @@ +module atmosphere_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + close_file, & + stdlog, stdout + +use transforms_mod, only: get_deg_lon, & + get_deg_lat, & + get_grid_boundaries, & + get_grid_domain, & + get_spec_domain, & + area_weighted_global_mean, & + atmosphere_domain + +use time_manager_mod, only: time_type, & + set_time, & + get_time, & + interval_alarm, & + operator(+), & + operator(<), & + operator(==) + +use barotropic_dynamics_mod, only: barotropic_dynamics_init, & + barotropic_dynamics, & + barotropic_dynamics_end, & + dynamics_type + +use barotropic_physics_mod, only: barotropic_physics_init, & + barotropic_physics, & + barotropic_physics_end, & + phys_type + +use diag_manager_mod, only: diag_manager_init, & + diag_manager_end + +use barotropic_diagnostics_mod, only: barotropic_diagnostics_init, & + barotropic_diagnostics + +!======================================================================== +implicit none +private +!======================================================================== + +! version information +!======================================================================== +character(len=128) :: version = '$Id: atmosphere.F90,v 17.0 2009/07/21 03:00:15 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +public :: atmosphere_init, & + atmosphere, & + atmosphere_end, & + atmosphere_domain + +!======================================================================== + +integer :: unit, seconds, days +integer :: pe, npes, itmp, m, n +integer :: previous, current, future +logical :: root + +integer :: dt_integer +real :: dt_real +type(time_type) :: dt_time_type, Time_init, Time_step + +real :: delta_t ! = 2*dt_real for leapfrog step + +type(phys_type), save :: Phys +type(dynamics_type), save :: Dyn + +integer :: is, ie, js, je, ms, me, ns, ne +integer :: num_lon, num_lat + +logical :: module_is_initialized =.false. + +integer :: print_interval=86400 + +! namelist +!======================================================================== +namelist /atmosphere_nml/ print_interval +!======================================================================== + +contains +!======================================================================= + +subroutine atmosphere_init(Time_init_in, Time, Time_step_in) + +type (time_type), intent(in) :: Time_init_in, Time, Time_step_in + +integer :: i, j, n, nn, ierr, io, unit +integer :: nlon, nlat +integer :: id_lon, id_lat, id_lonb, id_latb + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +Time_step = Time_step_in +call get_time( Time_step, seconds, days) +dt_integer = 86400*days + seconds +dt_real = float(dt_integer) +dt_time_type = Time_step +Time_init = Time_init_in + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=atmosphere_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'atmosphere_nml') + enddo + 10 call close_file (unit) +endif +call write_version_number(version, tagname) +if (root) write (stdlog(), nml=atmosphere_nml) + +call barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_lat, id_lonb, id_latb) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +num_lon = Dyn%num_lon +num_lat = Dyn%num_lat + +nlon = ie+1-is ! size of grid on each processor +nlat = je+1-js + +call barotropic_physics_init(Phys) +call barotropic_diagnostics_init(Time, num_lon, num_lat, id_lon, id_lat, id_lonb, id_latb) + +if(Time == Time_init) then + previous = 1 + current = 1 ! starting with a forward step before settling into leapfrog +else + previous = 1 + current = 2 +endif + +module_is_initialized = .true. + +return +end subroutine atmosphere_init + +!===================================================================== + +subroutine atmosphere(Time) + +type (time_type), intent(in) :: Time +integer :: day, second, dt +real :: energy, enstrophy + +if(.not.module_is_initialized) then + call error_mesg('atmosphere', & + 'atmosphere_init has not been called', FATAL) +end if + +call get_time(Time_step, second, day) +dt = second + 86400*day + +Dyn%Tend%u = 0.0 +Dyn%Tend%v = 0.0 +if(Dyn%grid_tracer) Dyn%Tend%tr = 0.0 +if(Dyn%spec_tracer) Dyn%Tend%trs = 0.0 + +if(Time == Time_init) then + delta_t = dt_real + future = 2 +else + delta_t = 2.0*dt_real + future = previous +endif + +call barotropic_physics(Time, & + Dyn%Tend%u, Dyn%Tend%v, & + Dyn%Grid%u, Dyn%Grid%v, & + delta_t, previous, current, & + Phys) + +call barotropic_dynamics(Time, Time_init, & + Dyn, previous, current, future, delta_t) + +previous = current +current = future + +call barotropic_diagnostics (Time+Time_step, Dyn, Phys, current) + +enstrophy = area_weighted_global_mean(Dyn%grid%vor(:,:,current)*Dyn%grid%vor(:,:,previous)) +energy = -area_weighted_global_mean(Dyn%grid%stream*Dyn%grid%vor(:,:,previous)) + +if(root) then + call get_time(Time+Time_step, second, day) + if(mod(second+86400*day, print_interval) < dt) then + write(stdout(),1000) day, second, energy, enstrophy + end if +end if +1000 format(1x, 'day =',i6,2x,'second =', i6,2x,'energy = ',e13.6,3x,'enstrophy = ',e13.6) + +return +end subroutine atmosphere + +!======================================================================================= + +subroutine atmosphere_end + +if(.not.module_is_initialized) then + call error_mesg('atmosphere_end', & + 'atmosphere_init has not been called.', FATAL) +end if + +call barotropic_physics_end (Phys) +call barotropic_dynamics_end (Dyn, previous, current) +module_is_initialized = .false. + +return +end subroutine atmosphere_end + +!======================================================================================= +end module atmosphere_mod diff --git a/src/atmos_spectral_barotropic/atmosphere.html b/src/atmos_spectral_barotropic/atmosphere.html new file mode 100644 index 000000000..7b937f1f1 --- /dev/null +++ b/src/atmos_spectral_barotropic/atmosphere.html @@ -0,0 +1,174 @@ + +module atmosphere_mod + + +
+PUBLIC INTERFACE / +DATA / +ROUTINES / +NAMELIST / +ERRORS + +


+ +

module atmosphere_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+ + +
+

OVERVIEW

+ +
+   A spectral transform model for two-dimensional, non-divergent flow on the
+   surface of the sphere.  
+
+
+ + +
+

DESCRIPTION

+ +
+   Integrates the barotropic vorticity equation for nondivergent flow on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the same spectral advection
+   algorithm as  the vorticity, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  The default initial condition 
+   provided as an example is a zonal flow resembling that in the Northern
+   winter,  plus a sinusoidal disurbance localized in midlatitudes.
+
+   For a full description of the model and algorithms used, see barotropic.ps
+
+   The interfaces in this module are the generic intefaces required by the
+   main program that can be used to drive various idealized atmospheric
+   models within FMS. Model resolution and related paramters are set in
+   namelists within the modules barotropic_xxx.
+
+ + + +
+

OTHER MODULES USED

+ +
+     fms_mod
+     constants_mod
+     transforms_mod
+     time_manager_mod
+     diag_manager_mod
+     barotropic_dynamics_mod
+     barotropic_physics_mod
+     barotopic_diagnostics_mod
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use atmosphere_mod [,only: atmosphere_init,       
+                             atmosphere,
+			     atmosphere_end]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+  There are no pubic data types
+ 
+
+
+ + +
+

PUBLIC ROUTINES

+ +
+
+subroutine atmosphere_init. Initializes the model.
+subroutine atmosphere.      Integrates forward one time step
+subroutine atmosphere_end.  Terminates model, cleaning up memory and finalizing diagnostics.
+
+
+
+
+ subroutine atmosphere_init(Time_init, Time, Time_step)
+
+    input:
+ 
+    type(time_type) :: Time_init -- Initial model time
+
+    type(time_type) :: Time      -- Model time
+
+    type(time_type) :: Time_step -- Time step
+       
+    When Time=Time_init, the first time step is a forward
+    step rather than leap frog because a cold start is assumed.
+
+    The FMS main program that runs the solo atmospheric models
+    obtains Time_init from the diag_table and Time from its namelist.
+
+
+
+ + + subroutine atmosphere(Time) + + input: + + type(time_type) :: Time -- Model time + + Integrates forward one time step + + +
+ + subroutine atmosphere_end(Atmos) + + No calling arguments. + + Terminates model, cleaning up memory and finalizing diagnostics + + +
+
+ + +

NAMELIST

+ +
+&atmosphere_nml
+
+   print_interval, integer : time interval in seconds 
+   between prints of global mean energy and enstrophy to standard output
+
+
+ + +
+

ERROR MESSAGES

+ +
+
+  Fatal error message if any public routine is called prior to atmosphere_init
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_barotropic/barotropic.pdf b/src/atmos_spectral_barotropic/barotropic.pdf new file mode 100644 index 0000000000000000000000000000000000000000..454e56389ee1c928fa8504acd2da98eec8ca41a4 GIT binary patch literal 80013 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 literal 0 HcmV?d00001 diff --git a/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 new file mode 100644 index 000000000..69cfc096e --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 @@ -0,0 +1,152 @@ + +module barotropic_diagnostics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: write_version_number, & + mpp_pe, & + mpp_root_pe, & + error_mesg, FATAL + +use transforms_mod, only: get_grid_domain, & + get_spec_domain, & + grid_domain, & + trans_spherical_to_grid + +use diag_manager_mod, only: register_diag_field, & + register_static_field, & + send_data + +use time_manager_mod, only: time_type, & + get_time + +use barotropic_physics_mod, only: phys_type +use barotropic_dynamics_mod, only: grid_type, dynamics_type + +implicit none +private + +public :: barotropic_diagnostics_init, & + barotropic_diagnostics + +character(len=84), parameter :: version = '$Id: barotropic_diagnostics.F90,v 17.0 2009/07/21 03:00:18 fms Exp $' +character(len=84), parameter :: tagname = '$Name: siena_201207 $' + +logical :: module_is_initialized = .false. + +character(len=8) :: axiset = 'barotropic' +character(len=84) :: mod_name = 'barotropic_diagnostics' + +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_trs, id_tr, id_eddy_vor, id_delta_u +integer :: is, ie, js, je, ms, me, ns, ne + +contains + +!----------------------------------------------------------------------------------------------------------------- +subroutine barotropic_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb) + +type(time_type), intent(in) :: Time +integer, intent(in) :: lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb + +integer, dimension(2) :: axis_2d + +integer :: log_unit +integer :: namelist_unit, ierr, io +logical :: used + +call write_version_number(version, tagname) + +call get_grid_domain(is, ie, js, je) +call get_spec_domain(ms, me, ns, ne) + +axis_2d(1) = id_lon +axis_2d(2) = id_lat + +id_u = register_diag_field(mod_name, 'ucomp' , axis_2d, Time, 'u_wind' , 'm/s' ) +id_v = register_diag_field(mod_name, 'vcomp' , axis_2d, Time, 'v_wind' , 'm/s' ) +id_vor = register_diag_field(mod_name, 'vor' , axis_2d, Time, 'relative vorticity', '1/s' ) +id_pv = register_diag_field(mod_name, 'pv' , axis_2d, Time, 'absolute vorticity', '1/s' ) +id_stream = register_diag_field(mod_name, 'stream' , axis_2d, Time, 'streamfunction' , 'm^2/s') +id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) +id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) +id_eddy_vor= register_diag_field(mod_name, 'eddy_vor', axis_2d, Time, 'eddy vorticity' , '1/s' ) +id_delta_u = register_diag_field(mod_name, 'delta_u' , axis_2d, Time, 'change in zonal wind','m/s' ) + +module_is_initialized = .true. + +return +end subroutine barotropic_diagnostics_init + +!-------------------------------------------------------------------------------------------- + +subroutine barotropic_diagnostics(Time, Dyn, Phys, time_index) + +type(time_type), intent(in) :: Time +type(phys_type), intent(in) :: Phys +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: time_index + +real, dimension(is:ie,js:je) :: grid_tmp +real, dimension(is:ie,js:je) :: delta_zonal_u +complex, dimension(ms:me,ns:ne) :: spec_tmp +logical :: used +integer :: j + +if(.not.module_is_initialized) call error_mesg('barotropic_diagnostics', & + 'barotropic_diagnostics_init has not been called', FATAL) + +if(id_u > 0) used = send_data(id_u , Dyn%Grid%u (:,:,time_index) , time) +if(id_v > 0) used = send_data(id_v , Dyn%Grid%v (:,:,time_index) , time) +if(id_vor > 0) used = send_data(id_vor , Dyn%Grid%vor (:,:,time_index) , time) +if(id_pv > 0) used = send_data(id_pv , Dyn%Grid%pv (:,:) , time) +if(id_stream > 0) used = send_data(id_stream , Dyn%Grid%stream (:,:) , time) +if(id_tr > 0) used = send_data(id_tr , Dyn%Grid%tr (:,:,time_index) , time) +if(id_trs > 0) used = send_data(id_trs , Dyn%Grid%trs (:,:,time_index) , time) +if(id_eddy_vor > 0) then + spec_tmp = Dyn%Spec%vor(:,:,time_index) + if(ms == 0) spec_tmp(0,:) = cmplx(0.0,0.0) + call trans_spherical_to_grid(spec_tmp, grid_tmp) + used = send_data(id_eddy_vor, grid_tmp, time) +endif +if(id_delta_u > 0) then + do j=js,je + delta_zonal_u(:,j) = sum(Dyn%Grid%u(:,j,time_index))/Dyn%num_lon - Dyn%Grid%zonal_u_init(j) + enddo + used = send_data(id_delta_u, delta_zonal_u, time) +endif + +return +end subroutine barotropic_diagnostics +!-------------------------------------------------------------------------------------------- + +subroutine barotropic_diagnostics_end(Time) + +type(time_type), intent(in) :: Time + +if(.not.module_is_initialized) call error_mesg('barotropic_diagnostics_end', & + 'barotropic_diagnostics_init has not been called', FATAL) + +module_is_initialized = .false. + +return +end subroutine barotropic_diagnostics_end +!-------------------------------------------------------------------------------------------- + +end module barotropic_diagnostics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_diagnostics.html b/src/atmos_spectral_barotropic/barotropic_diagnostics.html new file mode 100644 index 000000000..d852a921d --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_diagnostics.html @@ -0,0 +1,152 @@ + +module barotropic_diagnostics_mod + + +
+ + +

module barotropic_diagnostics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The diagnostics module for the model that solves the barotropic vorticity
+   equation on the sphere  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Using the diagnostics manager, creates output files for the barotropic model.
+   Variables currently available for output are
+       zonal wind 
+       meridional wind 
+       relative vorticity
+       absolute vorticity
+       streamfunction
+       spectral tracer in grid domain
+       grid tracer
+       
+   Whether or not these fields are actually output, the location of the 
+   output, the frequency of output, whether or not the output is averaged
+   in time or an instantaneous snapshot, is controlled by a 
+   diag_table file utilized by the diagnostics manager module
+       
+   One can add other diagnostics by following the (somewhat convoluted)
+       pattern within the program
+
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     diag_manaager_mod
+     transforms_mod
+     time_manager_mod
+     barotropic_dynamics_mod
+     barotropic_physics_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_diagnostics_mod [,only: barotropic_diagnostics_init,       
+                                         barotropic_diagnostics]
+                                
+
+
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_diagnostics_init
+subroutine  barotropic_diagnostics
+
+
+
+
+ subroutine barotropic_diagnostics_init(Time, num_lon, num_lat)
+ 
+   type(time_type)    , intent(in)     :: Time
+         current time 
+   integer, intent(in) :: num_lon, num_lat
+      num_lon = number of longitudes in global domain
+      num_lat = number of latitudes in global domain
+         
+
+   Initializes module
+
+
+
+
+ + + + subroutine barotropic_diagnostics (Time, Grid, Phys, time_index) + + type(time_type), intent(in) :: Time + type(phys_type), intent(in) :: Phys + type(grid_type), intent(in) :: Grid + integer, intent(in) :: time_index + + phys_type is defined in barotropic_physics_mod; + Phys is currently empty as there is no information generated in + barotropic_physics_mod to be output; + + grid_type is defined in barotropic_dynamics_mod: + Grid contains all of the fields to be output + + many of the grid fields in grid_type are dimensioned (lon, lat, time_index) + where time_index = 1 or 2 -- the two time levels needed to update the + state of the model using a leapfrog step are toggled between (:,:,1) + and (:,:,2). The input time_index (which must equal either 1 or 2) + determines which of these two fields is output) + + (this is confusing -- the calling program needs to know what has + been placed in which slot -- it would be better to store this + information within the data type) + + + + +
+ + + diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 new file mode 100644 index 000000000..07922fdbc --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 @@ -0,0 +1,573 @@ +module barotropic_dynamics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + mpp_error, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use time_manager_mod, only : time_type, & + get_time, & + operator(==), & + operator(-) + +use constants_mod, only: radius, omega + +use transforms_mod, only: transforms_init, transforms_end, & + get_grid_boundaries, & + trans_spherical_to_grid, trans_grid_to_spherical, & + compute_laplacian, & + get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_grid_domain, get_spec_domain, & + spectral_domain, grid_domain, & + vor_div_from_uv_grid, uv_grid_from_vor_div, & + horizontal_advection + +use spectral_damping_mod, only: spectral_damping_init, & + compute_spectral_damping + +use leapfrog_mod, only: leapfrog + +use fv_advection_mod, only: fv_advection_init, & + a_grid_horiz_advection + +use stirring_mod, only: stirring_init, stirring, stirring_end + +!=============================================================================================== +implicit none +private +!=============================================================================================== + +public :: barotropic_dynamics_init, & + barotropic_dynamics, & + barotropic_dynamics_end, & + dynamics_type, & + grid_type, & + spectral_type, & + tendency_type + + +! version information +!=================================================================== +character(len=128) :: version = '$Id: barotropic_dynamics.F90,v 17.0 2009/07/21 03:00:21 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!=================================================================== + +type grid_type + real, pointer, dimension(:,:,:) :: u=>NULL(), v=>NULL(), vor=>NULL(), trs=>NULL(), tr=>NULL() + real, pointer, dimension(:,:) :: pv=>NULL(), stream=>NULL() + real, pointer, dimension(:) :: zonal_u_init=>NULL() +end type +type spectral_type + complex, pointer, dimension(:,:,:) :: vor=>NULL(), trs=>NULL() +end type +type tendency_type + real, pointer, dimension(:,:) :: u=>NULL(), v=>NULL(), trs=>NULL(), tr=>NULL() +end type +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat + logical :: grid_tracer, spec_tracer +end type + +integer, parameter :: num_time_levels = 2 + +integer :: is, ie, js, je, ms, me, ns, ne + +logical :: module_is_initialized = .false. + +real, allocatable, dimension(:) :: sin_lat, cos_lat, rad_lat, rad_lon, & + deg_lat, deg_lon, & + coriolis, glon_bnd, glat_bnd + +integer :: pe, npes + +! namelist parameters with default values + +logical :: check_fourier_imag = .false. +logical :: south_to_north = .true. +logical :: triang_trunc = .true. + +real :: robert_coeff = 0.04 +real :: longitude_origin = 0.0 + +character(len=64) :: damping_option = 'resolution_dependent' +integer :: damping_order = 4 +real :: damping_coeff = 1.e-04 +real :: damping_coeff_r = 0.0 + +real :: zeta_0 = 8.e-05 +integer :: m_0 = 4 +real :: eddy_width = 15.0 +real :: eddy_lat = 45.0 + +logical :: spec_tracer = .true. +logical :: grid_tracer = .true. + +integer :: num_lat = 128 +integer :: num_lon = 256 +integer :: num_fourier = 85 +integer :: num_spherical = 86 +integer :: fourier_inc = 1 + +real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) +character(len=64) :: initial_zonal_wind = 'two_jets' + +namelist /barotropic_dynamics_nml/ check_fourier_imag, south_to_north, & + triang_trunc, & + num_lon, num_lat, num_fourier, & + num_spherical, fourier_inc, & + longitude_origin, damping_option, & + damping_order, damping_coeff, & + damping_coeff_r, robert_coeff, & + spec_tracer, grid_tracer, & + eddy_lat, eddy_width, zeta_0, m_0, & + valid_range_v, initial_zonal_wind + +contains + +!=============================================================================================== + +subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_lat, id_lonb, id_latb) + +type(dynamics_type), intent(inout) :: Dyn +type(time_type) , intent(in) :: Time, Time_init +real, intent(in) :: dt_real +integer, intent(out) :: id_lon, id_lat, id_lonb, id_latb + +integer :: i, j + +real, allocatable, dimension(:) :: glon_bnd, glat_bnd +complex, allocatable, dimension(:,:) :: div +real :: xx, yy, dd + +integer :: ierr, io, unit, pe +logical :: root + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=barotropic_dynamics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'barotropic_dynamics_nml') + enddo + 10 call close_file (unit) +endif + + +if (root) write (stdlog(), nml=barotropic_dynamics_nml) + +call transforms_init(radius, num_lat, num_lon, num_fourier, fourier_inc, num_spherical, & + south_to_north=south_to_north, & + triang_trunc=triang_trunc, & + longitude_origin=longitude_origin ) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +Dyn%num_lon = num_lon +Dyn%num_lat = num_lat +Dyn%spec_tracer = spec_tracer +Dyn%grid_tracer = grid_tracer + +allocate (sin_lat (js:je)) +allocate (cos_lat (js:je)) +allocate (deg_lat (js:je)) +allocate (deg_lon (is:ie)) +allocate (rad_lat (js:je)) +allocate (rad_lon (is:ie)) +allocate (coriolis (js:je)) + +allocate (glon_bnd (num_lon + 1)) +allocate (glat_bnd (num_lat + 1)) + +call get_deg_lon (deg_lon) +call get_deg_lat (deg_lat) +call get_sin_lat (sin_lat) +call get_cos_lat (cos_lat) +call get_grid_boundaries (glon_bnd, glat_bnd, global=.true.) + +coriolis = 2*omega*sin_lat + +rad_lat = deg_lat*atan(1.0)/45.0 +rad_lon = deg_lon*atan(1.0)/45.0 + +call spectral_damping_init(damping_coeff, damping_order, damping_option, num_fourier, num_spherical, 1, 0., 0., 0., & + damping_coeff_r=damping_coeff_r) +call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) + +allocate (Dyn%spec%vor (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%grid%u (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%v (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%vor (is:ie, js:je, num_time_levels)) + +allocate (Dyn%tend%u (is:ie, js:je)) +allocate (Dyn%tend%v (is:ie, js:je)) +allocate (Dyn%grid%stream (is:ie, js:je)) +allocate (Dyn%grid%pv (is:ie, js:je)) +allocate (Dyn%grid%zonal_u_init(js:je)) + +allocate (div (ms:me, ns:ne)) + +call fv_advection_init(num_lon, num_lat, glat_bnd, 360./float(fourier_inc)) +if(Dyn%grid_tracer) then + allocate(Dyn%Grid%tr (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%tr (is:ie, js:je)) +endif + +if(Dyn%spec_tracer) then + allocate(Dyn%Grid%trs (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%trs (is:ie, js:je)) + allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) +endif + +if(trim(initial_zonal_wind) == 'zero') then + Dyn%grid%zonal_u_init = 0.0 +else if(trim(initial_zonal_wind) == 'two_jets') then + do j = js, je + Dyn%grid%zonal_u_init(j) = 25.0*cos_lat(j) & + - 30.0*(cos_lat(j)**3) & + + 300.0*(sin_lat(j)**2)*(cos_lat(j)**6) + enddo +else + call error_mesg('barotropic_dynamics_init',trim(initial_zonal_wind)// & + ' is not a valid value of initial_zonal_wind ', FATAL) +endif + +if(Time == Time_init) then + + do j = js, je + Dyn%Grid%u(:,j,1) = Dyn%grid%zonal_u_init(j) + Dyn%Grid%v(:,j,1) = 0.0 + end do + + call vor_div_from_uv_grid(Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1), & + Dyn%Spec%vor(:,:,1), div) + + call trans_spherical_to_grid(Dyn%Spec%vor(:,:,1), Dyn%Grid%vor(:,:,1)) + + do j = js, je + do i = is, ie + yy = (deg_lat(j)- eddy_lat)/eddy_width + Dyn%Grid%vor(i,j,1) = Dyn%Grid%vor(i,j,1) + & + 0.5*zeta_0*cos_lat(j)*exp(-yy*yy)*cos(m_0*rad_lon(i)) + end do + end do + + call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) + + div = (0.,0.) + call uv_grid_from_vor_div (Dyn%Spec%vor(:,:,1), div, & + Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1)) + + if(Dyn%grid_tracer) then + Dyn%Grid%tr = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%tr(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%tr(:,j,1) = -1.0 + end do + endif + + if(Dyn%spec_tracer) then + Dyn%Grid%trs = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%trs(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%trs(:,j,1) = -1.0 + end do + call trans_grid_to_spherical(Dyn%Grid%trs(:,:,1), Dyn%Spec%trs(:,:,1)) + endif + +else + + call read_restart(Dyn) + +endif + +module_is_initialized = .true. + +return +end subroutine barotropic_dynamics_init + +!=============================================================================================== + +subroutine barotropic_dynamics(Time, Time_init, Dyn, previous, current, future, delta_t) + +type(time_type) , intent(in) :: Time, Time_init +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in ) :: previous, current, future +real, intent(in ) :: delta_t + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +complex, dimension(ms:me, ns:ne) :: dt_vors, dt_divs, stream, zeros, spec_diss +real, dimension(is:ie, js:je) :: dt_vorg +integer :: j, seconds, days + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +if(.not.module_is_initialized) then + call error_mesg('barotropic_dynamics','dynamics has not been initialized ', FATAL) +endif + +zeros = (0.,0.) + +do j = js, je + Dyn%grid%pv(:,j) = Dyn%grid%vor(:,j,current) + coriolis(j) +end do + +Dyn%Tend%u = Dyn%Tend%u + Dyn%grid%pv*Dyn%Grid%v(:,:,current) +Dyn%Tend%v = Dyn%Tend%v - Dyn%grid%pv*Dyn%Grid%u(:,:,current) + +call vor_div_from_uv_grid (Dyn%Tend%u, Dyn%Tend%v, dt_vors, dt_divs) + +call compute_spectral_damping(Dyn%Spec%vor(:,:,previous), dt_vors, delta_t) + +call stirring(Time, dt_vors) + +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff) + +call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) + +call uv_grid_from_vor_div (Dyn%Spec%vor (:,:,future), zeros, & + Dyn%Grid%u (:,:,future), Dyn%Grid%v (:,:,future)) + +if(minval(Dyn%Grid%v) < valid_range_v(1) .or. maxval(Dyn%Grid%v) > valid_range_v(2)) then + call get_time (Time, seconds, days) + call mpp_error(FATAL,'barotropic_dynamics: Meridional wind out of valid range. Model time=',days,' days ',seconds,' seconds') +endif + +if(Dyn%spec_tracer) call update_spec_tracer(Dyn%Spec%trs, Dyn%Grid%trs, Dyn%Tend%trs, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +if(Dyn%grid_tracer) call update_grid_tracer(Dyn%Grid%tr, Dyn%Tend%tr, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) +call trans_spherical_to_grid(stream, Dyn%grid%stream) + +return +end subroutine barotropic_dynamics + +!=================================================================================== + +subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & + previous, current, future, delta_t) + +complex, intent(inout), dimension(ms:me, ns:ne, num_time_levels) :: tr_spec +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +complex, dimension(ms:me, ns:ne) :: dt_trs + +call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) +call trans_grid_to_spherical (dt_tr, dt_trs) +call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff) +call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) + +return +end subroutine update_spec_tracer +!========================================================================== + +subroutine update_grid_tracer(tr_grid, dt_tr_grid, ug, vg, & + previous, current, future, delta_t) + +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr_grid +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg + +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +real, dimension(size(tr_grid,1),size(tr_grid,2)) :: tr_current, tr_future + +tr_future = tr_grid(:,:,previous) + delta_t*dt_tr_grid +dt_tr_grid = 0.0 +call a_grid_horiz_advection (ug(:,:,current), vg(:,:,current), tr_future, delta_t, dt_tr_grid) +tr_future = tr_future + delta_t*dt_tr_grid +tr_current = tr_grid(:,:,current) + & + robert_coeff*(tr_grid(:,:,previous) + tr_future - 2.0*tr_grid(:,:,current)) +tr_grid(:,:,current) = tr_current +tr_grid(:,:,future) = tr_future + +return +end subroutine update_grid_tracer + +!========================================================================== + +subroutine read_restart(Dyn) + +type(dynamics_type), intent(inout) :: Dyn + +integer :: unit, m, n, nt +real, dimension(ms:me, ns:ne) :: real_part, imag_part + +if(file_exist('INPUT/barotropic_dynamics.res.nc')) then + do nt = 1, 2 + call read_data('INPUT/barotropic_dynamics.res.nc', 'vors_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'vors_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%vor(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + if(Dyn%spec_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%trs(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + endif + call read_data('INPUT/barotropic_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nt), grid_domain, timelevel=nt) + if(Dyn%spec_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nt), grid_domain, timelevel=nt) + endif + if(Dyn%grid_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nt), grid_domain, timelevel=nt) + endif + end do +else if(file_exist('INPUT/barotropic_dynamics.res')) then + unit = open_restart_file(file='INPUT/barotropic_dynamics.res',action='read') + + do nt = 1, 2 + call set_domain(spectral_domain) + call read_data(unit,Dyn%Spec%vor(:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Spec%trs(:,:, nt)) + + call set_domain(grid_domain) + call read_data(unit,Dyn%Grid%u (:,:, nt)) + call read_data(unit,Dyn%Grid%v (:,:, nt)) + call read_data(unit,Dyn%Grid%vor (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Grid%trs(:,:, nt)) + if(Dyn%grid_tracer) call read_data(unit,Dyn%Grid%tr (:,:, nt)) + + end do + call close_file(unit) + +else + call error_mesg('read_restart', 'restart does not exist', FATAL) +endif + +return +end subroutine read_restart + +!==================================================================== + +subroutine write_restart(Dyn, previous, current) + +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: previous, current + +integer :: unit, nt, nn + +do nt = 1, 2 + if(nt == 1) nn = previous + if(nt == 2) nn = current + call write_data('RESTART/barotropic_dynamics.res.nc', 'vors_real', real(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'vors_imag', aimag(Dyn%Spec%vor(:,:,nn)), spectral_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs_real', real(Dyn%Spec%trs(:,:,nn)), spectral_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs_imag', aimag(Dyn%Spec%trs(:,:,nn)), spectral_domain) + endif + call write_data('RESTART/barotropic_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nn), grid_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nn), grid_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nn), grid_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nn), grid_domain) + endif + if(Dyn%grid_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nn), grid_domain) + endif +enddo + +!unit = open_restart_file(file='RESTART/barotropic_dynamics.res', action='write') + +!do nt = 1, 2 +! if(nt == 1) nn = previous +! if(nt == 2) nn = current + +! call set_domain(spectral_domain) +! call write_data(unit,Dyn%Spec%vor(:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Spec%trs(:,:, nn)) + +! call set_domain(grid_domain) +! call write_data(unit,Dyn%Grid%u (:,:, nn)) +! call write_data(unit,Dyn%Grid%v (:,:, nn)) +! call write_data(unit,Dyn%Grid%vor (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Grid%trs(:,:, nn)) +! if(Dyn%grid_tracer) call write_data(unit,Dyn%Grid%tr (:,:, nn)) +!end do + +!call close_file(unit) + +end subroutine write_restart + +!==================================================================== + +subroutine barotropic_dynamics_end (Dyn, previous, current) + +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in) :: previous, current + +if(.not.module_is_initialized) then + call error_mesg('barotropic_dynamics','dynamics has not been initialized ', FATAL) +endif + +call transforms_end() +call stirring_end() + +call write_restart (Dyn, previous, current) + +module_is_initialized = .false. + +return +end subroutine barotropic_dynamics_end +!=================================================================================== + +end module barotropic_dynamics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.html b/src/atmos_spectral_barotropic/barotropic_dynamics.html new file mode 100644 index 000000000..62c97898a --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.html @@ -0,0 +1,371 @@ + +module barotropic_dynamics_mod + + +
+ + +

module barotropic_dynamics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The dynamical core of the spectral transform model for 
+   two-dimensional, non-divergent flow on the surface of the sphere.  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Integrates the barotropic vorticity equation for nondivergent flow on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the same spectral advection
+   algorithm as  the vorticity, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  The default initial condition 
+   provided as an example is a zonal flow resembling that in the Northern
+   winter,  plus a sinusoidal disurbance localized in midlatitudes.
+
+   For a full description of the model and algorithms used, see 
+     barotropic.ps 
+   
+   For higher level routines for running this barotropic spectral model,
+   see  atmosphere_mod 
+
+
+
+ + + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     constants_mod
+     time_manager_mod
+     transforms_mod
+     spectral_damping_mod
+     leapfrog_mod
+     fv_advection_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_dynamics_mod [,only: barotropic_dynamics_init,       
+                                      barotropic_dynamics,
+			              barotropic_dynamics_end,
+                                      dynamics_type,
+				      grid_type,
+				      spectral_type,
+				      tendency_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type grid_type + real, pointer, dimension(:,:,:) :: u, v, vor, trs, tr, pv + real, pointer, dimension(:,:) :: stream +end type + + allocated space for grid fields + + (:,:,:) => (lon, lat, time_level) + (:,:) => (lon, lat) + (lon, lat) on local computational domain + time_level stores the two time levels needed for the + leapfrog step + + u -- eastward velocity (m/s) + v -- northward velocity (m/s) + vor -- vorticity (1/s) + trs -- tracer advected spectrally + tr -- tracer advected on grid + pv -- absolute vorticity, f + vor, where f = 2*omega*sin(lat) (1/s) + stream -- streamfunction (m^2/s) at current time + + + +
+ + +type spectral_type + complex, pointer, dimension(:,:,:) :: vor, trs +end type + + allocated space for spectral fields + + (:,:,:) => (zonal, meridional, time_level) + + vor -- spectral vorticity + trs -- spectral tracer + +
+
+ +type tendency_type + real, pointer, dimension(:,:) :: u, v, trs, tr +end type + + allocated space for accumulating tendencies, d/dt, in grid space, + for prognostic variables + + (:,:,:) => (lon, lat) + +
+
+ +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat ! size of global domain + logical :: grid_tracer, spec_tracer +end type + + grid_tracer = .true. => tracer with gridpoint advection is beign integrated + similarly for spec_tracer + +
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_dynamics_init
+subroutine  barotropic _dynamics
+subroutine  barotropic_dynamics_end
+type (grid_type)
+type (spectral_type)
+type (tendency_type)
+type (dynamics_type)
+
+
+
+
+
+ subroutine barotropic_dynamics_init(Dyn,  Time, Time_init)
+ 
+   type(dynamics_type), intent(inout)  :: Dyn
+         type containing all dynamical fields and related information
+	 (see type (dynamics_type))
+	 
+   type(time_type)    , intent(in)     :: Time, Time_init
+         current time and time at which integeration began
+	 time_type defined by time_manager_mod
+         
+
+   Initializes the module;
+   Reads restart from 'INPUT/barotropic_dynamics.res' if Time = Time_init;
+     otherwise uses default initial conditions
+
+
+
+
+ + + + subroutine barotropic_dynamics & + (Time, Time_init, Dyn, previous, current, future, delta_t) + + type(time_type) , intent(inout) :: Time, Time_init + type(dynamics_type), intent(inout) :: Dyn + integer , intent(in ) :: previous, current, future + real , intent(in ) :: delta_t + + previous, current and future = 1 or 2 + these integers refer to the third dimension of the + three-dimensional fields in Dyn + the fields at time t - delta_t are assumed to be in (:,:,previous) + the fields at time t are assumed to be in (:,:,current) + the fields at time t + delta_t are placed in (:,:,future) + overwriting whatever is already there + + delta_t = time step in seconds + + updates dynamical fields by one time step + + + +
+ + + subroutine barotropic_dynamics_end(Dyn, previous, current) + + type(dynamics_type), intent(inout) :: Dyn + integer, intent(in) :: previous, current + + + Terminates module; + writes restart file to 'RESTART/barotropic_dynamics.res' + + + + +
+
+ + + +
+

NAMELIST

+ +
+
+&barotropic_dynamics_nml
+
+  integer :: num_lat            = 128  
+        number of latitudes in global grid
+       
+  integer :: num_lon            = 256
+        number of longitudes in global grid
+        should equal 2*num_lat for Triangular truncation
+  
+  integer :: num_fourier        = 85
+        the retained fourier wavenumber are n*fourier_inc, where
+        n ranges from 0 to num_fourier
+	 
+  integer :: num_spherical      = 86
+        the maximum number of meridional modes for any zonal wavenumber
+        for triangular truncation, set num_spherical = num_fourier +1
+         
+  integer :: fourier_inc        = 1
+        creates a "sector" model if fourier_inc > 1; integration domain is
+	(360 degrees longitude)/fourier_inc
+	
+  (the default values listed above define a standard T85 model)
+
+  logical :: check_fourier_imag = .false.
+        if true, checks to see if fields to be transformed to grid 
+	domain have zero imaginary part to their zonally symmetric
+	modes; useful for debugging
+	
+  logical :: south_to_north     = .true.
+        true => grid runs from south to north
+	false => grid runs from north to south
+	
+  logical :: triangular_trunc   = .true.
+        true  => shape of truncation is triangular
+	false => shape of truncation is rhomboidal
+
+  real    :: robert_coeff       = 0.04
+        x(current) => (1-2r)*x(current) + r*(x(future)+x(previous))
+	where r = robert_coeff (non-dimensional)
+	
+  real    :: longitude_origin   = 0.0
+        longitude of first longitude, in degrees
+	(if you want the westgern boundary of first grid boc to be at 
+         0.0, set longitude_origin = 0.5*360./float(num_lon))
+	 
+  integer :: damping_option     = 'resolution_dependent'
+  integer :: damping_order      = 4
+  real    :: damping_coeff      = 1.e-04
+  
+        damping = nu*(del^2)^n where n = damping order
+	damping_option = 'resolution_dependent' or 'resolution_independent'
+	  = 'resolution_dependent' => nu is set so that the damping rate for the 
+	        mode (m=0,n=num_spherical-1) equals damping_coeff (in 1/s)
+	        For triangular truncation, damping_coeff is then the 
+	        rate of damping of the highest retained mode
+	     
+	  = 'resolution_independent' => nu = damping_coef
+	
+	
+  real     :: zeta_0     = 8.e-05
+  integer  :: m_0        = 4
+  real     :: eddy_width = 15.0
+  real     :: eddy_lat   = 45.0
+  
+         eddy component of the initial condition is sinusoidal with
+	 wavenumber m_0 and with a gaussian distribution of 
+	 vorticity in latitude, centered at eddy_lat with half-width
+	 eddy_width
+	 
+	 zeta_0 ( 1/s)
+	 eddy_width and eddy_lat (degrees)
+
+  logical :: spec_tracer      = .true.
+  logical :: grid_tracer      = .true.
+       spec_tracer = true => a passive tracer is carried that is advected
+          spectrally, with the same algorithm as the vorticity
+       grid_tracer = ture => a passive tracer is carried that is advected
+          on the spectral transform grid by a finite-volume algorithm
+	  (see  barotropic.ps )
+       Both tracers can be carried simultaeneously
+	  
+The vorticity and the tracers are initialized within subroutine
+     barotropic_dynamics_init
+
+  real, dimension(2) :: valid_range_v = -1000., 1000.
+        A valid range for meridional wind. Model terminates if meridional wind
+        goes outside the valid range. Allows model to terminate gracefully when,
+        for example, the model becomes numerically unstable.
+
+
+ + + +
+

ERROR MESSAGES

+ +
+
+   "Dynamics has not been initialized"
+      -- barotropic_dynamics_init must be called before any other
+         routines in the module are called
+	 
+   "restart does not exist" 
+      -- Time is not equal to Time_init at initalization, but the file
+          'INPUT/barotropic_dynamics.res' does not exit 
+	 
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_barotropic/barotropic_physics.F90 b/src/atmos_spectral_barotropic/barotropic_physics.F90 new file mode 100644 index 000000000..8590bacce --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_physics.F90 @@ -0,0 +1,173 @@ +module barotropic_physics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + fms_init, fms_end, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use transforms_mod, only: get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_wts_lat, & + get_grid_domain, get_spec_domain, & + grid_domain + +use time_manager_mod, only: time_type + +!======================================================================== +implicit none +private +!======================================================================== + +public :: barotropic_physics_init, & + barotropic_physics, & + barotropic_physics_end, & + phys_type + +! version information +!======================================================================== +character(len=128) :: version = '$Id: barotropic_physics.F90,v 10.0 2003/10/24 22:00:58 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +type phys_type + real, pointer, dimension(:,:) :: empty=>NULL() +end type + +logical :: module_is_initialized = .false. + +integer :: is, ie, js, je + + +integer :: pe +logical :: root + +real, allocatable, dimension(:) :: rad_lat, & + deg_lat, & + sin_lat, & + cos_lat, & + wts_lat + +! namelist +!======================================================================== + +logical :: empty + +namelist /barotropic_physics_nml/ empty +!======================================================================== + +contains + +!======================================================================== + +subroutine barotropic_physics_init(Phys) + +type(phys_type), intent(inout) :: Phys + +integer :: j, unit, ierr, io + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=barotropic_physics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'barotropic_physics_nml') + enddo + 10 call close_file (unit) +endif + +call get_grid_domain(is,ie,js,je) + +allocate ( rad_lat (js:je) ) +allocate ( deg_lat (js:je) ) +allocate ( sin_lat (js:je) ) +allocate ( cos_lat (js:je) ) +allocate ( wts_lat (js:je) ) + +call get_wts_lat(wts_lat) +call get_deg_lat(deg_lat) +rad_lat = deg_lat*atan(1.)/45. +sin_lat = sin(rad_lat) +cos_lat = cos(rad_lat) + +module_is_initialized = .true. + +return +end subroutine barotropic_physics_init + +!======================================================================= + +subroutine barotropic_physics(Time, dt_ug, dt_vg, ug, vg, & + delta_t, previous, current, Phys) + +real, intent(inout), dimension(is:ie, js:je) :: dt_ug, dt_vg +real, intent(in) , dimension(is:ie, js:je, 2) :: ug, vg + +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +type(time_type), intent(in) :: Time +type(phys_type), intent(inout) :: Phys + +if(.not.module_is_initialized) call error_mesg('barotropic_physics', & + 'barotropic_physics is not initialized', FATAL) + +! dt_ug = dt_ug +f(ug,vg) +! dt_vg = dt_vg +f(ug,vg) +! Phys%empty = + +return +end subroutine barotropic_physics + +!====================================================================== + +subroutine barotropic_physics_end(Phys) + +type(phys_type), intent(in) :: Phys + +if(.not.module_is_initialized) call error_mesg('barotropic_physics_end', & + 'barotropic_physics is not initialized', FATAL) + +module_is_initialized = .false. +return +end subroutine barotropic_physics_end + +!====================================================================== + +end module barotropic_physics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_physics.html b/src/atmos_spectral_barotropic/barotropic_physics.html new file mode 100644 index 000000000..e391ca50f --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_physics.html @@ -0,0 +1,165 @@ + +module barotropic_physics_mod + + +
+ + +

module barotropic_physics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the barotropic model on the sphere
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the barotropic model on the sphere.  Currently,
+   does nothing!
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     transforms_mod
+     time_manager_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_physics_mod [,only: barotropic_physics_init,       
+                                         barotropic_physics,
+					 barotropic_physics_end,
+					 phys_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type phys_type + real, pointer, dimension(:,:) :: empty +end type + + fields from physics module made available for diagnostics + +
+
+ +
+ + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_physics_init
+subroutine  barotropic_physics
+subroutine  barotropic_physics_end
+type(phys_type)
+
+
+
+
+
+ subroutine barotropic_physics_init(Phys)
+ 
+   type(phys_type)    , intent(inout)     :: Phys
+  
+ 
+   Initializes module
+
+
+
+
+ + + + subroutine barotropic_physics (Time, dt_ug, dt_vg, ug, vg, & + delta_t, previous, current, Phys) + + real, intent(inout), dimension(:,:) :: dt_ug, dt_vg + + the u and v tendencies onto which tendencies due to + the grid-point physics are added (m/(s^2)) + + real, intent(in) , dimension(:,:, 2) :: ug, vg + the grid zonal and meridional velocities (m/s) + the third index is the time-index used in the leapfrog step + + real , intent(in) :: delta_t + time step (s) + + integer, intent(in) :: previous, current + = 1 or 2 + ug(:,:,previous) is the velocity at t-delta_t + ug(:,:,current ) is the velocity at t + + type(time_type), intent(in) :: Time + type(phys_type), intent(inout) :: Phys + + + + +
+ + + + subroutine barotropic_physics_end (Phys) + + type(phys_type), intent(inout) :: Phys + + + + +
+ + + diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 new file mode 100644 index 000000000..04bdfc280 --- /dev/null +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -0,0 +1,252 @@ +module stirring_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +! Stirring is computed as described in the following paper: + +! Vallis, Gerber, Kushner, Cash, 2003: A Mechanism and Simple Dynamical Model of the North Atlantic Oscillation and Annular Modes. +! J. Atmos. Sci., 61, 264-280. + +! Stirring is not part of barotropic_physics because barotropic_physics appears to be intended for +! operations that are done completely in grid space. Stirring is computed partly in spectral space. + +use constants_mod, only: pi + +use time_manager_mod, only: time_type + +use fms_mod, only: open_namelist_file, check_nml_error, close_file, write_version_number, & + stdlog, mpp_pe, mpp_root_pe, file_exist, read_data, write_data, error_mesg, FATAL + +use transforms_mod, only: get_spec_domain, get_grid_domain, trans_spherical_to_grid, trans_grid_to_spherical, & + grid_domain, get_lon_max, get_lat_max, get_deg_lon, get_deg_lat, get_grid_boundaries, & + get_num_fourier, get_num_spherical, spectral_domain + +use diag_manager_mod, only: diag_axis_init, register_static_field, register_diag_field, send_data + +implicit none +private + +integer :: ms,me,ns,ne,is,ie,js,je +integer :: id_str_amp, id_g_stir_sqr, id_stir +logical :: used +logical, allocatable, dimension(:,:) :: wave_mask ! wave_mask(m,n) = .true. if spherical wave (m,n) is to be excited +complex, allocatable, dimension(:,:) :: s_stir ! stirring. Saved from one time step to the next +real, allocatable, dimension(:,:) :: localize ! localizes the stirring +real, allocatable, dimension(:,:) :: g_stir_sqr ! time mean of g_stir**2 over entire integration +integer, allocatable, dimension(:) :: seed ! random number seed +real :: astir, bstir +integer :: num_steps, num_fourier, num_spherical, nseed +character(len=8) :: axiset = 'barotropic' +character(len=84) :: mod_name = 'barotropic_diagnostics' + +logical :: module_is_initialized = .false. + +character(len=128) :: version = '$Id: stirring.F90,v 17.0 2009/07/21 03:00:25 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' + +public :: stirring_init, stirring, stirring_end + +real :: decay_time=2*86400, amplitude=0.0, lat0=45., widthy=12. + +! Set B to a non-zero value for stirring that has zonal structure. +! The strength of the stirring at latitude=lat0 is: amplitude*(1.0 + B*exp(-.5*((lon-lon0)/widthx)**2)) +real :: lon0=180., B=0.0, widthx=45. ! widthx + +namelist / stirring_nml / decay_time, amplitude, lat0, lon0, widthy, widthx, B + +contains + +!================================================================================================================================ +subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) +real, intent(in) :: dt +type(time_type), intent(in) :: Time +integer, intent(out) :: id_lon, id_lat, id_lonb, id_latb +real :: xx, kk, rad_to_deg +integer :: i,j,m,n,ierr,io,unit,lon_max,lat_max +real, allocatable, dimension(:) :: ampx, ampy, lon, lat, lonb, latb +real, allocatable, dimension(:,:) :: real_part, imag_part + +if(module_is_initialized) return + +call write_version_number (version, tagname) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=stirring_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'stirring_nml') + enddo + 10 call close_file (unit) +endif +if(mpp_pe() == mpp_root_pe()) write(stdlog(), nml=stirring_nml) + +call get_lon_max(lon_max) +call get_lat_max(lat_max) + +allocate(lon (lon_max )) ; lon = 0.0 +allocate(lat (lat_max )) ; lat = 0.0 +allocate(lonb(lon_max+1)) ; lonb = 0.0 +allocate(latb(lat_max+1)) ; latb = 0.0 + +call get_deg_lon(lon) +call get_deg_lat(lat) +call get_grid_boundaries(lonb,latb,global=.true.) + +rad_to_deg = 180./pi +id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name='barotropic', Domain2=grid_domain) +id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name='barotropic', Domain2=grid_domain) +id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name='barotropic', Domain2=grid_domain, edges=id_lonb) +id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name='barotropic', Domain2=grid_domain, edges=id_latb) + +module_is_initialized = .true. +if(amplitude == 0.0) return ! stirring does nothing more unless amplitude is non-zero + +call get_spec_domain(ms,me,ns,ne) +call get_grid_domain(is,ie,js,je) +call get_num_fourier(num_fourier) +call get_num_spherical(num_spherical) + +allocate(wave_mask(ms:me,ns:ne)); wave_mask = .false. +allocate(s_stir(ms:me,ns:ne)); s_stir = cmplx(0.0,0.0) +allocate(ampx(is:ie)); ampx = 0.0 +allocate(ampy(js:je)); ampy = 0.0 +allocate(localize(is:ie,js:je)); localize = 0.0 +allocate(g_stir_sqr(is:ie,js:je)); g_stir_sqr = 0.0 + +! wave_mask is .true. when (m+n > 9) .and. (m+n < 15) .and. (m > 3) +do m=4,14 + if(m >= ms .and. m <= me) then + do n=10-m,14-m + if(n >= ns .and. n <= ne) then + wave_mask(m,n) = .true. + endif + enddo + endif +enddo + +astir = sqrt(1.0 - exp(-2*dt/decay_time)) +bstir = exp(-dt/decay_time) + +do i=is,ie + xx = lon(i)-lon0 + ! Make sure xx falls in the range -180. to +180. + kk = nint(xx/360.) + xx = xx - 360.*kk + ampx(i) = (1 + B*exp(-.5*(xx/widthx)**2)) +enddo +do j=js,je + ampy(j) = exp(-.5*((lat(j)-lat0)/widthy)**2) +enddo +do j=js,je +do i=is,ie + localize(i,j) = ampx(i)*ampy(j) +enddo +enddo +deallocate(ampx, ampy) + +num_steps = 0 +id_g_stir_sqr = register_static_field('stirring_mod', 'stirring_sqr', (/id_lon,id_lat/), 'stirring sqrared', '1/sec^4') +id_str_amp = register_static_field('stirring_mod', 'stirring_amp', (/id_lon,id_lat/), 'amplitude of stirring', 'none') +id_stir = register_diag_field ('stirring_mod', 'stirring', (/id_lon,id_lat/), Time, 'stirring', '1/sec^2') +used = send_data(id_str_amp, amplitude*localize) + +call random_seed(size=nseed) +allocate(seed(nseed)) + +if(file_exist('INPUT/stirring.res.nc')) then + allocate(real_part(ms:me,ns:ne), imag_part(ms:me,ns:ne)) + call read_data('INPUT/stirring.res.nc', 'stir_real', real_part, spectral_domain) + call read_data('INPUT/stirring.res.nc', 'stir_imag', imag_part, spectral_domain) + do n=ns,ne + do m=ms,me + s_stir(m,n) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + deallocate(real_part, imag_part) + call read_data('INPUT/stirring.res.nc', 'ran_nmbr_seed', seed, no_domain=.true.) + call random_seed(put=seed) +endif + +end subroutine stirring_init +!================================================================================================================================ +subroutine stirring(Time, dt_vors) +type(time_type), intent(in) :: Time +complex, dimension(ms:me,ns:ne), intent(inout) :: dt_vors +real, dimension(is:ie,js:je) :: g_stir +complex, dimension(ms:me,ns:ne) :: new_stirring +real, dimension(0:num_fourier,0:num_spherical,2) :: ran_nmbrs +integer :: i,j,m,n +real :: x,y + +if(.not.module_is_initialized) then + call error_mesg('stirring', 'stirring_init has not been called', FATAL) +end if + +if(amplitude == 0.0) return ! stirring does nothing unless amplitude is non-zero + +call random_number(ran_nmbrs) + +do n=ns,ne +do m=ms,me + if(wave_mask(m,n)) then + new_stirring(m,n) = amplitude*astir*cmplx(2*ran_nmbrs(m,n,1)-1, 2*ran_nmbrs(m,n,2)-1) + else + new_stirring(m,n) = cmplx(0.0,0.0) + endif +enddo +enddo +call trans_spherical_to_grid(new_stirring,g_stir) +g_stir = localize*g_stir +call trans_grid_to_spherical(g_stir,new_stirring) +if(ms == 0 .and. ns == 0) then + new_stirring(0,0)=cmplx(0.0,0.0) ! A non-zero global mean is introduced by the grid space computation, but we don't want it. +endif +s_stir = bstir*s_stir + new_stirring + +dt_vors = dt_vors + s_stir +call trans_spherical_to_grid(s_stir,g_stir) +g_stir_sqr = g_stir_sqr + g_stir*g_stir +num_steps = num_steps + 1 +used = send_data(id_stir, g_stir, Time) + +end subroutine stirring +!================================================================================================================================ +subroutine stirring_end + +if(.not.module_is_initialized) return + +if(amplitude == 0.0) return ! stirring does nothing unless amplitude is non-zero + +g_stir_sqr = g_stir_sqr/num_steps +used = send_data(id_g_stir_sqr, g_stir_sqr) + +call write_data('RESTART/stirring.res.nc', 'stir_real', real(s_stir), spectral_domain) +call write_data('RESTART/stirring.res.nc', 'stir_imag', aimag(s_stir), spectral_domain) +call random_seed(get=seed) +call write_data('RESTART/stirring.res.nc', 'ran_nmbr_seed', seed, no_domain=.true.) + +deallocate(wave_mask, s_stir, localize, g_stir_sqr) +module_is_initialized = .false. + +end subroutine stirring_end +!================================================================================================================================ + +end module stirring_mod diff --git a/src/atmos_spectral_shallow/atmosphere.F90 b/src/atmos_spectral_shallow/atmosphere.F90 new file mode 100644 index 000000000..3b59b37fb --- /dev/null +++ b/src/atmos_spectral_shallow/atmosphere.F90 @@ -0,0 +1,279 @@ +Module atmosphere_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +!========================================================================= + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + close_file, & + stdlog + +use mpp_mod, only: mpp_max + +use constants_mod, only: radius_earth => radius, & + omega_earth => omega + +use transforms_mod, only : get_deg_lon, & + get_deg_lat, & + get_grid_boundaries, & + get_grid_domain, & + get_spec_domain, & + area_weighted_global_mean, & + atmosphere_domain + +use time_manager_mod, only : time_type, & + set_time, & + get_time, & + interval_alarm, & + operator(+), & + operator(<), & + operator(==) + +use shallow_dynamics_mod, only : shallow_dynamics_init, & + shallow_dynamics, & + shallow_dynamics_end, & + dynamics_type + +use shallow_physics_mod, only : shallow_physics_init, & + shallow_physics, & + shallow_physics_end, & + phys_type + +use shallow_diagnostics_mod, only : shallow_diagnostics_init, & + shallow_diagnostics + + +!======================================================================== +implicit none +private +!======================================================================== + +! version information +!======================================================================== +character(len=128) :: version = '$Id: atmosphere.F90,v 14.0 2007/03/15 22:13:18 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +public :: atmosphere_init, & + atmosphere, & + atmosphere_end, & + atmosphere_domain + +!======================================================================== + +integer, parameter :: num_time_levels = 2 + +integer :: unit, seconds, days +integer :: pe, npes +integer :: previous, current, future +logical :: root + +integer :: dt_integer +real :: dt_real +type(time_type) :: dt_time_type, Time_init, Time_step + +real :: delta_t ! = 2*dt_real for leapfrog step + +integer, dimension(2) :: axes + +type(phys_type), save :: Phys +type(dynamics_type), save :: Dyn + +integer :: is, ie, js, je, ms, me, ns, ne +integer :: num_lon, num_lat +integer, dimension(4) :: axis_id ! axes identifiers + +logical :: module_is_initialized =.false. + + +integer :: print_interval +! namelist +!======================================================================== +namelist /atmosphere_nml/ print_interval +!======================================================================== + +contains +!======================================================================= + +subroutine atmosphere_init(Time_init_in, Time, Time_step_in) + +type (time_type), intent(in) :: Time_init_in, Time, Time_step_in + +integer :: i, j, n, nn, ierr, io, unit +integer :: nlon, nlat + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +Time_step = Time_step_in +call get_time(Time_step, seconds, days) +dt_integer = 86400*days + seconds +dt_real = float(dt_integer) +dt_time_type = Time_step +Time_init = Time_init_in + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=atmosphere_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'atmosphere_nml') + enddo + 10 call close_file (unit) +endif +call write_version_number(version, tagname) +if (root) write (stdlog(), nml=atmosphere_nml) + +call shallow_dynamics_init (Dyn, Time, Time_init) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +num_lon = Dyn%num_lon +num_lat = Dyn%num_lat + +nlon = ie+1-is ! size of grid on each processor +nlat = je+1-js + +call shallow_physics_init(Phys) +call shallow_diagnostics_init(Time, num_lon, num_lat) + +if(Time == Time_init) then + previous = 1 + current = 1 +else + previous = 1 + current = 2 +endif + +module_is_initialized = .true. + +return +end subroutine atmosphere_init + +!===================================================================== + +subroutine atmosphere(Time) + +type (time_type), intent(in) :: Time +integer :: day, second, dt + + +if(.not.module_is_initialized) then + call error_mesg('atmosphere', & + 'atmosphere_init has not been called', FATAL) +end if + +call get_time(Time_step, second, day) +dt = second + 86400*day + +Dyn%Tend%u = 0.0 +Dyn%Tend%v = 0.0 +Dyn%Tend%h = 0.0 +if(Dyn%grid_tracer) Dyn%Tend%tr = 0.0 +if(Dyn%spec_tracer) Dyn%Tend%trs = 0.0 + +if(Time == Time_init) then + delta_t = dt_real + future = 2 +else + delta_t = 2.0*dt_real + future = previous +endif + +call shallow_physics(Time, & + Dyn%Tend%u, Dyn%Tend%v, Dyn%Tend%h, & + Dyn%Grid%u, Dyn%Grid%v, Dyn%Grid%h, & + delta_t, previous, current, & + Phys) + +call shallow_dynamics(Time, Time_init, & + Dyn, previous, current, future, delta_t) + +previous = current +current = future + +call shallow_diagnostics (Time+Time_step, Dyn%Grid, Phys, current) + +call get_time(Time+Time_step, second, day) +if(mod(second+86400*day, print_interval) < dt) & + call global_diag(second, day, current) + +return +end subroutine atmosphere + +!======================================================================================= + +subroutine global_diag(second, day, current) + +integer, intent(in) :: second, day, current + +real :: enstrophy, div_squared, max_Froude +real, dimension(size(Dyn%Grid%u,1), size(Dyn%Grid%u,2)) :: speed + +enstrophy = & +area_weighted_global_mean(Dyn%grid%vor(:,:,current)*Dyn%grid%vor(:,:,current)) + +div_squared = & +area_weighted_global_mean(Dyn%grid%div(:,:,current)*Dyn%grid%div(:,:,current)) + +speed = Dyn%Grid%u(:,:,current)*Dyn%Grid%u(:,:,current) +& + Dyn%Grid%v(:,:,current)*Dyn%Grid%v(:,:,current) +max_Froude = maxval(speed/Dyn%Grid%h(:,:,current)) +call mpp_max(max_Froude) + +if(root) then + write(*,1000) day, second, enstrophy, div_squared, max_Froude +end if +1000 format(1x, 'day =',i6,2x,'second =', i6, & + 2x,'enstrophy = ',e13.6,3x,'div_squared = ',e13.6, 3x, & + 'max_Froude = ', e10.3) + +return +end subroutine global_diag + +!=============================================================================== +subroutine atmosphere_end + +if(.not.module_is_initialized) then + call error_mesg('atmosphere_end', & + 'atmosphere_init has not been called.', FATAL) +end if + +call shallow_physics_end (Phys) +call shallow_dynamics_end (Dyn, previous, current) + +module_is_initialized = .false. + +return +end subroutine atmosphere_end + +!======================================================================================= +end module atmosphere_mod diff --git a/src/atmos_spectral_shallow/atmosphere.html b/src/atmos_spectral_shallow/atmosphere.html new file mode 100644 index 000000000..13e269faf --- /dev/null +++ b/src/atmos_spectral_shallow/atmosphere.html @@ -0,0 +1,164 @@ + +module atmosphere_mod + + +
+ +

module atmosphere_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+ + +
+

OVERVIEW

+ +
+   A spectral transform model for the shallow water equations on the sphere
+
+
+ + +
+

DESCRIPTION

+ +
+   Integrates the shallow water equations for hydrostatic flow in a thin layer
+   of homogeneous fluid on the sphere, using the spectral transform technique.
+   Also allows for the inclusion of a passive tracer advected by the 
+   spectral advection algorithm as the vorticity, and a gridpoint tracer 
+   advected with a finite volume algorithm on the transform grid.  
+   The default experiment is forced by a "monsoonal" mass source, starting
+   from a state of rest.
+   
+   For a full description of the model and algorithms used, see shallow.ps
+
+   The interfaces in this module are the generic intefaces required by the
+   main program that can be used to drive various idealized atmospheric
+   models within FMS. Model resolution and related parameters are set in
+   namelists within the modules shallow_xxx.
+
+ + + +
+

OTHER MODULES USED

+ +
+     fms_mod
+     constants_mod
+     transforms_mod
+     time_manager_mod
+     diag_manager_mod
+     shallow_dynamics_mod
+     shallow_physics_mod
+     shallow_diagnostics_mod
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+  use atmosphere_mod [,only: atmosphere_init,       
+                             atmosphere,
+			     atmosphere_end]
+
+
+ + +
+

PUBLIC DATA

+ +
+  There are no public data types
+
+
+ + +
+

PUBLIC ROUTINES

+ +
+subroutine atmosphere_init. Initializes the model.
+subroutine atmosphere.      Integrates forward one time step
+subroutine atmosphere_end.  Terminates model, cleaning up memory and finalizing diagnostics.
+
+
+
+ subroutine atmosphere_init(Time_init, Time, Time_step)
+
+   input:
+
+   type(time_type) :: Time_init -- Initial model time
+
+   type(time_type) :: Time      -- Model time
+
+   type(time_type) :: Time_step -- Time step
+
+   When Time=Time_init, the first time step is a forward
+   step rather than leap frog because a cold start is assumed.
+
+   The FMS main program that runs the solo atmospheric models
+   obtains Time_init from the diag_table and Time from its namelist.
+       
+
+
+ + + subroutine atmosphere(Time) + + input: + + type(time_type) :: Time -- Model time + + Integrates forward one time step + + +
+ + subroutine atmosphere_end + + No calling arguments. + + Terminates model, cleaning up memory and finalizing diagnostics + +
+
+ + +

NAMELIST

+ +
+&atmosphere_nml
+
+   print_interval, integer : time interval in seconds 
+   between prints of global mean energy and enstrophy to standard output
+
+
+ + +
+

ERROR MESSAGES

+ +
+  Fatal error message if subroutine atmosphere or atmosphere_end
+  is called prior to atmosphere_init.
+
+
+ +
+ + diff --git a/src/atmos_spectral_shallow/shallow.pdf b/src/atmos_spectral_shallow/shallow.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3ae5dbd1a0157a5e7710577d64dc696e69f1a415 GIT binary patch literal 62941 zcmbTdV|XUr`t2LrcAnUF$L!d)ZQC|Gw$ZVZjyvi&9ox2j`hEB6vwEGi_WrMZzRjvR zYgW~D)pI?+F~)t9$%}~5G0`)_knNrnx0LCnJ1*~AgRAZBggY$9S}WM^yw!^a2X708tq2(p`X~QSBnBZN#xt-o-rc3OuIvac-31AJ;tJMcb0b({zRcDnYRN+p=5wGHb zQaQMqzU|!0=}Aj(*SCc&sS)9Z?!m;tByVR?lQR>Tnrq57pbX+?{E*C5@UtX{AxP7W z<6Tv{@Mlh}<2|RCiM=pd-xySSooZ}2lttM(Z{m|*9@q%=g<1V!9&Mb=OLzL~VWpOm z&gJ~vYmYOIN~1v2BMd3x z>lfgH4*PYj_;yyqst4LDXAJ+zjV$Z>e3iW?Gx_wO0pnCeyK>9ovkW5NCzccIP}DO$ zm}H=<1W$j)Vn#j?yBd}RaJr^h-ZdlMg|+z!0IC3%({W#A^nlV zvD4HtN@^eyUpl>vb?4BK2C{)kyX055I|8CLweJUw0?Q)lTmuek)`%ERf$nrCh_&Ik z<{K!+zaBbY-TB-gZ7L{9{*!rB(h&;<2aJXi#^d@ z{j1wn;7&zu6A#IU=H|sn0jQk#mPp#j!Xov!3Fn$1#<}y?QSe67mbY2~)WtKhnwyeZ zF+4h+HZZk}hIEkNeMc$$4!oxtc5RI7`9Mm~>a}k|&;_HG>kyG*bC1_DCfX+PU-@LF5qW2<1Rn+)>HL175G?lU=)c^ z#V<6Y2DrT>!HF{mn-)4`el~K#iDnq!vYkKKkX(@q(J-J_ExW0HOpT8b-gKqQ2( ztFB4N;kHmULPhoXaH|+?YyLcU@fFF*1a;7c|5k1zXclGcM#r4`o>U&Ij0yRvC=`Y9 z+iFeYqol$6L{Qf!?p}D^LH5q!gZ;p7F=&l(B}Q1ZyKSPOSh4~6PY(n$#d9a6k=Mr; zOh1b74Opbl(?HEA*Lt1UAxk>N88T8Ta-3KsGi%{NqihP`P%EoA9cU$CD0uMMh-l_j zjIM31zt|?=k`crDk=R3<8;nIb_hHXH$(I+y#&6BL7h|vyglg{u0C6tNaVT9wRl@Q~ zfdz!dr;YM5!kz$ba`W}*!@hW1(k_ryoh`TXc3l9p8JEn@x>eMRgxi|n8EY_4mSTsz zOXJa!oq5b+ag96Ych|TZ1b);PB!R4NOF!k}ZL^z;B-dAT@ zz>T~Yg>OSM5>&Ba4#}$d;P-&9oMe2zM#v$H1h{e-$`^^3h9CMGE-z9TS-SFt0{tos z9ubN8T_!!sE=th9C4?@|h)=YLJD$g;naSbsp2^ng;?c9?fv{J~e~`I|WGqYjijV`R z3+fXlv<`Mw&`P~+^vnS1}s{(1svo~bPLte@iZ!> z&vmz$RvoE#($AFb*nXhhKf!V`B!je?5Z9&HurLP&Q>@;juCOPKMBaQRx;(- zK1)^}v-HeL4^eK8@4^K{@j)DZwy{D+!3w)>+Z? zF6^8M2=7B|L}YUCF&4q!n50~Si{hpdh5o8`F)6`$)PVxOcmP@rc@x>hD4dQm8K&4{N%D4k00R`#+$%%&BWIDAAIp^^ovSh znEs#>5r7uUuR}!ugSv($fPnbvCheb^`oGJ&G_4iY88WE{;YfP5|crbwb|2@z*?n>96}L{;}a-)4w;A zceFE7GI0iIG5q2w0E4oLyEA}6!sgcvgmivk)300nf~jBJ#LNs}V*G2Lzs9TpuHQo@ z0Ovo3Out^>_mC67{(Hy{VEcRci?;sxO25|r^AUc78Yes3{|st#I8shK%}?ig`6I=S z96}Ks?&76!MHbAnN8e`&=DBK7Y*+6cWwb+PSpf_T`kTDaqP2*H*u-*K}9baA;q- zJjm;Q*x&H-sP-GtN6gfYkDtG_mrJQz478WIor9RNBYuCMSUZbCG&yMrm(7bt$$uqS zDd`$7YvpeeRb#tLy(}^HHS^EsB#;Zc#sZ>lCun4^GN((fgr5s6!xIw(+q}Dbad8{J zea807IEk6U0qV;{d{k=w%(1ZAhMX5)Y6%G>PCY-V^02w@X5v%T4x?Wi&WJ-~$TD|%9CpI{x0j}AGYM+<_4-b>EMwTqJQEx(L z#e(yLXSP~5`W&)KIY2eyPkAB+Ym-i5m@Iuj`z)Sj+2VLfzT(+egy)lR)mpK1@$vR) z{_u9=PlV)9!CyNBvpcYDAB_31w|ha)CS3DZe7(5r6V_Pc-)vq>g?6*R0>q%07-m6xpk&`f38B!I1C`a(Sh)i;(!&7;T9YI9dtPS^T~!`S z{lTWLQnICQT)7GNMdn_n#@Yn-nk-o<``uL1%KhL-v*4^O77;@?tquHgO~s(M>*;Rp zI!K}ke>6L&xmsa8m^hr;)&!&q z$+Px|A6*mJ82Vg%$s$;DUtov)sK@nG9cN1ioh-&|97;cyFdXx!GzgC*r2~pGeyAXa znrGPV|Rl4T=6xz=~nFW3E{_*9s}<|ADGV*X5w8j z;i-{7$gH&iH&X3y(Ba|?q}y2jv`EN>kTN!`d(sgOZg0Rj`*4>OavyfJLC8cDfCD_wD{Ph^^3_W+JjrPZ3G={7E%@(R~lknB5I_ znXASTkAanjbq}0{tM<8)3rIkucTy94it=Ms5(aOA^Bh;?)sZ(gb}smU;TRg_8o#MTO8&$g zzF(qea>Hf-;OwSZncvs5H$!E|g8g9C7|ly7fw*p2U=A8X@O3RcvyW+m1w{?x_^veP zlO|k*E;jOwi%p~X{iVr(T_(6tZphKUSNErknHByy0`Wdvb+oi7KJK8C&}ZcPm0=8b zLJXZ<96f2tp@OlWC(7>>PlG>37@cB3vEQ@e83OL0+y)booDBOCFrd);$zx&EE zL8(B#DtFp6V&jphbUyB~SR*y1ncstrl(G0R-S`3OOc}pPWiUc!>og(7EHBL4`W=dm znl<55-#F#ZU{>3=ZKyWlz4`LPlft3o`alMwgaiHZPKivGiXt1*%BdJ~F0lX;Nd$8y zEG``(8rqyIIQ=AOPf&F8$!2h^-s#IbG{4AiegpxIwFtVj3Y~g9#hli3-??BIOPHAO zW0BF$2K$)C7Q;y;fWM%v3fgXv?P1VEcz8)Jr*{obp}Qzb76`qBC~iBX0s@iGd7PT= zSkIS{__p6!d#Lw`dsrHwoRGrW z#1c;yafUmrRE`?j#X2T~?n%6cusHYu7LOW{Y1|UuaO=P*l}<2TApVrZjg|cw3>b8< zibD&4RFu6GE`-U63I{cXuNT?(ly;rHgAV&(N@02)w z`6iwdJ>O8h@pH>TgWXeqvE6Q)ll3*o!pdORRAWa4eLM5q*GVQZs3YGB6I9YU9~S%N zi`98#M%#i4UJ2%Ko*mM&^F~N}tY+yS?bcB~b8V8P28+W6iW-(GL)8pv+lC8ahWWXX zn9VUShbRo4L&jqhVJ<0tjOE3HR&NyWiz;J3pY56{r(NeT#OFEaqSlgpTuz*L{Ou1Y zk8sLhexRQl3PGSRZ>@k>;f>UgC|`<{l6Kro2}=3h_e?*xgUeYsK(AoRvi_WE9>T2x z5w&m6tXNMWKy0meA;-pm>P3r=++YWVsDM9Xtx6TfvF_&Uq3{c4ZC_epB|Lw1V~l4` z)v@a6v0qC$>*aSF7nQm+{n)K{OLN7$%yPI0OI~4&!_@|mcy_7Wx%w>=l}MYN810Bv zxos^zy$El;q=%qmrU}+{er&2Obl5xiEt9C7agX;V?K#`CP}ZBAwBP9x8_Ywm-z2~Z z_#D7~gy15`93oq10^igVxWS%h+{d57N@_iQQc}qiS|t9;2x3*JzPq3<50BcgtF=6) zy$b_=;*tWcmM+sOFt#gANLo@A>Gw#lff&_AXsA)I;$$FrN;UWO7!%dY{_1i>V&r6z&)={7jlttpSeTxTQ3XbNY<<}j92ha7#RXe z-3f)I{sHN$SH~UZF1<)W-*QI*B2fXv^}+}`9cz5rJnSzI=3QUi?emp_e^La3H_34N zNP;Xde1u#9KCfk)yG?zvnH>Wb6rUX@zI)lDI)-f3R$9Vl3KFT)%l5z9|g$YIR7sSgz0Z_1M^F%{AUUB_x$fY|62{h z1Yr6tHkjD}OuyB}pK|06mBPdYVEU~>nE#L|zXb_{*#E0o{zaSoQab;pO_(^j{tFX% zTtmxlg&oazvSzCAn<}l{wuxwfjW~B+Pc82w-BHjnWdz62v2llB9e=lzdje&Y1MuQOvXFdypqT(8rHZ}x&w9Z`g;n6z+E!qO_q6~yh-s)(2j>$Mg7(dsHy zwo#R$-#Rq9w7&UVg*N18QpsY`NSca+gE{2f^cHZHMrnt5swpO@j$uYUt7pVk%9~y(GeQ*zoTA-<&j?>SR z={3Jn5{#fFFbHY6{8-pN#^S@y0CzhotU(fUPzaZPYpK?y8TS#%r zriL;?er-K9IR0D`{`1>*O}&0%0VHbiTK#U8QId=r=;UpWR&diTBdvr=Hoy>!itj1S zgug4>*E#|rC^x{6_3Y8fpCk_T%jU}rPOku*7zt9VG1O9`7BP)k)zHg$^U=2xTn^l@ z$dV2#*Ar6+B&DaG53v{nMu%Wx^5-q)UI?T>F`-)f40uD(&N#==SS4$qH7Iy)g`V(x zNaB=TJViLe^IQN~h)`hiMS~Rf7ur3wV{+QQEl`Bv+6Er72#jGRgF!R@yHADegn3uA zmb>~28Ic+TP*O~Nk60u!3(|K%H&Ov9X+{cJZqS=~)CMOB2q0YDW>#O^yA^Y?i$sJ0 zEXwDnVX;aVLr0g!vJ`?^~D14kbW}QGx6SqTF?f&G-091!e`%NZ= zS<3?H0`f4qkSrAz*^4=8_omTSbtC)mVb@m$f7pJ%Y?b<1ISsDKu+35|h!Z+9PU)k35Wz@TJ(tGYRdHOX3O9sl=J7vm z_`-o`!llPR4iA+T^FY@R5G1+px9BBz4&!IZl!wbme=?w`lMICiinELy$%ac_Q6TeA zLcDJ0x;S@hdj}NaH3MNDOd)4Q0|$d z2Cj@`!HOX$l1Hf{+|hj81Aq1yx)(fNwplL_T%51$0FRZh?lZj%a=4x4FY$l2TK?|k zO}l(HZu1SHKW$Jf3HU4DTFDu&RZoFju`Cge*8H2VNn#Og!`Kc8Qr*f#@ zi(lK0u&%>3-S5!rlsznH@+NAIqbKNPUwCaU%=E>=;xX{CBXqD*t6(uzhjQM<+`Tkd zu7mviO!q`OvD~L%n5^C<(I^)A=Rw*Be@O5mVgi;EwSl|1g8*_TOGxbOA_Gxxh#-ww z!I`x$4c1^0!?0Z`4_nqm9M_iy$H$BqrRC)D3gd01O$V;_g1QKBu|5jvx1Ygs_k3P# z0Z|j_`h}GS*Z{S3==*o~CY|%HiyA$4a!qZAq`C-53&EQ*&*TQq{Su%z1#Rm=#)m&Xu$E~!sIxPr_p zFuQ#CWYallzA6w?dEh(Y-eWd)i@v!dCqEUyn^5X%2zDB(lkX`DC&yislErZ1f}~9~ zu;DI3qfF1Xt0bITCSWJ(1oV%;HjkwuhW&EHl9Sp~-cDR-MlOBRpo}QDy=2ok-#X#D zJl7-*T)i!C!r$}C9rlVvlq&H=$IW48$D`+*=7i|OW&LOjsVU4xw)A^);;*Kch`*2} z7W*>*g{~!&=KTXp=(dGYaw*D)+{KY%JAK@Ri zDibH?e_^YBQ`d;wWkvGM)k_#+of>q-;}+A5FbdEiCGh{^zPJ=7?dJK(n!+OSHu`=; zX|LYNxjsk$WE`2$q(gg1O$7rvj1Ug1fN9ERbr|eNdc+;_rVr}*6=)uec~>dq zr;&6CyS+F|%yZexBb~M#AC}fSu;L&=$|I1vRMuVe9`nC+am@rF+?Ii6ZqB(ka`zCF zI)3ESw@)O=D5p4<6jQ*qn23;n7ynX63lhLglhxR@ZXGbrA@26@h7i5;xi*>^h3A=T zTUvCoc1P~=;q&b|uQ!653u%=;IdDQh{im1;amODb>~L9ywwC&R)TRkM3A9S)hFd>< zJ)jeqjQl?zrFrcwsiA_5C~w<&Omf+6<*J{PkV!QNV7%Cjan`8gh&(qYCm75;FLBWD z$b!Y}leMZq$RcCkX30plT7t-up~Jw~fgI~(5u?OvTMN1mB7bu=#g69-IJ9501hUsgj&IG5$qGd#(hPu*Duu${@3#JCT~!&)?l8N9F6y!Qobjl zfqsgUMo}bmWpx4_PoKcxgZGw`I1qhw=Mm8SH=wa*Lj7rmIxVa5n^p=sa=a`bN;~z0 zVmsQd)Vi$|2{?Qd9!z{gRi?bIt9-O!#!I3hL%zKC0EI)u2;d}(%U1@qbDAY7sG2l{**tc}u`d&zIa!%4 zUBK@;egK-#oTxL_Wd*A*@bDe-?7?dl4*RZ@6F(WO?1bu2yK-knAr1`PS(-Ax~?_x?t5(j^FWyi#G+o~;Cs4p9`(`ZyEr^ih#!iNP%zzxo_JSGBBHR>K1oFgV_!i( zJ~x7tz-{`G(KShsBiFyRTjC54;()sV@ADa3|DtXjaW4(D59-XaAOBPl!PNs^sxRy7 zNhU1YVQ5l%o3d&PO~)04ECZZ#a?zC?JkhEC5Y$B5q~whgX30vuUQh+;)yR&2rxkE! zC9wDan7Fnp$dLrdFd#P5u^YXX-!5*ZCsI3OipcIRcE z5GXasDH2eMqvN}rl$455CcTOEJ3L%9@v+lcN0R;el`iiCA6vHO1uS3K@KQyu5l4hH z#08s69AViyoG(9xvbZetxd&_*@Zw#mbKn=1{47Q3q;uk2w3AS zf#HLNp&sAXpMgC7gkSv53yS-M4W=YKNN|mgmnCLaa7>Q+{pr-Jw9iiM{{7@A!^*Gi zxvp>)P_MYZbqt!BYIVY}fS&CI6OK_sW_N`|1pqIrV4|kkTj#Pj^uBR0e*75(Z|1Ei zy`fSo^(u}$Km);&mxI|YpuvTEa?fX>&bK?Jmk^!vjabm_Inuann6hL2VO1&lO7wej zhUZXIptg|8*FXxrh0S>gK#v#gf>g9wZm?I(2-kRExJikc9yq|AY+tmsPCaO~K7W46 z{adrQKgne126zC!fwr`ZK)i^Tu{p2eHo*{Ts0`{~751MJ)d-ApJWCrsH7ZWTj_f{yPii z`VZjpXK?zT!R2qy<6qL?f8@jeJ1^#!fAUXi_^-j`pAj$fKf&csE9U>ga$)%kt^PmU z^550%ooPte?TBG?y{KJM7JrreYODeQ48bCS18urr1PF6QXjD$*$`=nST3&m)yV0d1 z6`hHlJtH6$*T-&Ru=~(WAVYgsPjq;kHg;<-7`ZwoT%o~$P?jW>VlO97muxProa{EB zHi%ART_Q;2s65Nou@fOz{mP4;x$Ufn(NS=tgEi{acZGAk6@QQC01%r<0`O+Jk?At3YJ>*1?fIpuY^f+G11LxlH3El7o z8&r*d{_X}enIZ)uVEAJ5qkANiFn>``r8b6?M~Yn&g(y*}kCP$~BwvU8OdWjNm_(wz z43q=yyl$Nm;NYd!_ zPR`)7Q0}VZr3JBN)`QN6;j5b;o*vquAePvER=X2_(JrBu&hh!A^1);FNz3b#BdxKhgLR4 z@RNdyc(dT8q~n9yb{(C<>A1%-1d?{w@)jFGbvgbwiPQ`Pr)!vYQQxwqzk9C5_+8o}6JE5(npEMtQOTpLC2G)6T`1Xk# z6P(U$Wg<@)vO>{|x0^31*?2qR1lEw)MUS5LKtxv6$$oplnA0*`AR>OOHOh~7V;u>; z<2!T>27-U@Z5yOFH^0EPI{#MG6L6+qgoC^=~F6NZdxudWk`B{Ax-&w5(kfq;MA(XV#Le+B{vj(^Zld@54&cWLqywqo@E<>lrfG#N7ijB=s#r8ucY0q-9ZddT?FV&3J{Otuw zMEny4VL%%KM7yQpol&ZrxID~}_(~f+=n5!49U?sqK&G5{6By=7hn25au)e`o1y2{_ z31jJY^n3ivdPrMfc&4wqS?~f=Z{5DLJ7FOy6sGdO0e=?>_(7W?A!= zzFW)21S@;oB+(P_BsBQM5)dA7NK|Tg*lTjT2~rfxGedsKYBVur_tp_yKHPsn2~=6&@-fG^ol};)*DWD^cxj zu@M4F#2r48$=%pb11Pygj&EaR>`Npd&XIy*Vn&Y-w0$+SX1zaf+9D=uM$#}`nm6I; zb>U`=Y$Bp!a@G zbxwZcM&Bt+#<23q*`U5kIwN`|CRsM5*l`u?+ZU z&9pDNA7CV{QRIcejGZU$d5sS-Koy-p`OH294%xG%313=SOlx>HTKHOU?bPRV>;h-a zW$^*{^BeMl2eBLa(Gh{$QVPLFk%wlNPh64acc*2w;oNXt9pH;sEyZ|lcp0kNcL?0$ zSz2MMtCs}h^rZPXL=47?hX}kR>XF;0{qdc{Uy8D>c2Ld@HIHsz-D zPLnUCNlc^gQ30bNVkc|jP$K?%dT`;);N^9v36y0bpOm1tpdy$kv0#UF=q;gSpt~EB zrNGmRX0JmwXa`Ri1TP`;w0okjL2i7u)#6fnk8)9tctLl$7g`fM0|@h?S$#BGKU!@i z#vH@cLZc<%YIsicuzB(Mn_k?InVDq@{hM`s`QZ9tQn>ysA-|4K{}UuZppH9SU)HOp#>pd18;!CH&g5j zB3~Z~r8sWA{vFu{v)vc4L|?tH2s}&{U4mB zzvh2G&wmSSe;4nf(!y{Xug7IVNNIlNx`Q@cn|<|G9+kOhYPZry03xvSw4k zF_baKTY0paw5iTWEiuX^^DcnIL2T?-wS{8Cp5KKnt3V(r`FeK6B46vhAP+A5=E->t zQG)#=NuqY2Ue4*H8;?iV%gVWID}qXGAE||@x^3dx)AT-JW8 zJ|BA=iqhl6Nwf%7+QLc)FN*feOgwcI?`>rTB6J!>0owa+cJ&Ph-?uQXfI z6odv-50@AGBmHrgT&p@LBj7A1i5OV{15U)?P&D3e$+!9Mi8Nn=$OEZlp2H#wEX$`e z4Zh|LYp9ZgC%HoEwaFao`x|>MgB(~Px4u1L$N}6B>Ep-^JEvzV`qqx%CjDLQuDGq(^Zm~39mXe$E zamnx7VGj|so$un^Ngmo)BrGd#)9>F5hWpG>O9+IfasaX9Lj8JS!VP^xqj*++R<(>5 zE4)*l*GDRslKHNlElYP~jVrz@@22c?F79pgaHqs+s`&iUaTBhpqt-cxb6! ziJqcMR|a<8_=?G(QBH9vd(WiRkI@-INw6k#1mR5cLgf%Y=wc7u_Qh_T0{^pgDO!%O zz2v;4Qm+gBbXpMyc{%HjsQtP_X{poT52W7Q*1WN(MR;-eE;Co)hHsPf`qvCM-0@E< z=lc0}PY@jBx=u!RAeGEjQ%Si>H2gyv5;@O1b?3)eIp{&2_T0IV$LR!FCbP1>z1Hve z8UlOWat+WNZLFC{I*w>0Xgh|9vsLW4kEsA2Sx;lEfpWd7mUtGG32P1v{R0YQg&9Mq zawAGXCY9hH)-4tW%t2VQ!F5kdr9yjf?ASo(EYVJ~2z{CFtd!&PX@W-&2SBRf8MliA zX|YUBH;A@%0~31a7Ujvz1>s=ZRD6VT#qboSa1_``E40B=`~~z<7_=|aYV}Z;>USgs zOt4vo?-0>+*7B*d%sOwuozgdbD8jF*;!(9zd~FD%9L)l(G|LNYlb@v+$~{HiY|8Br z?mQLcL}=-Nt(cu}fLrF4t&dabF5iy9V#JN=%5e^*q=XIhQ0qdp9E3ynPoHrBi5^s& zLVjgBNk$_iqTkt#8=dm!j@rNn-FW1Jk%dwUz3AXh$`)H%Y~yeTBG(#iZ0HHSwGPU* znu2taO2!Oe6ETuPIzA9E8Ha<%7uflry%Pha}=mbt=n2h=1(~GaRBBdt&EpV=`cq{x~KhvW-x5HgL9L@S5?j^6KyFH+$+%i+-<9+QcWjCjz;9FcBh6jKDKBqNZy3pm` zfY!R&0VZE?@m5B=q`m#H74)rjrDcp!Xd?JIC#w(w?{95*IE)m*O-M!s7AY{cKvh(& zz^uciq6E!65$P^n&6sG6IrGfOJfRW}teKSlqLcx-A8LuE>f}VeH`n9Q?y^SjXnU9* z>?}kn$kXC3$V76A#5LNV@xAfe1;FUlfBA->sF1GCvH*{Ig;6$IJwHGXbbJYsW4|oP zg<7DYkB9@*WKWJ^B!fz*(u5P#+XRAM@lR@TxDBB~yoX?t2N6k=N)bvYJ6pN4NAo}^ zCvlgqr?}la4ryJdT=j4eQ1(kHY1&tC>Q>!7`(zlR@%m`cDhoc6cO4w4NjO0i>>BW8kw-|$Y0`CRRAKAC-NQ| z641aQERrV8!Io0G!j1U=Ozj-po3+Nl@w%RVZN)Qa)~R|DH`oUa1t0)7l$tV26%D#J zTDcG{Zd#q(iPa>ljug%|f0{zLzrRaP^G~!kZKucTO-HoB9BI0qvJ~qWHQGHQ6YGk< zjGLVfFb(lDOZ$L9LU;u~F}AYr7c#m$wu{490z-qs7qYcw5Y*lzn?=HmrpKN-#o^YHJgAq2N}iMII5@>yxr0UL|@LSk>A3ey0L$V2r7W0 zu#JrIeJzROK^Ye2ThN8Cy)5W{)4^yQZivgB{mH2vhJ1uZ%@`c29iv|I0)3NEJSX$> z+UESOklWj)s}3b>u*HILU-IbpwWyJjo;tgia8vBj+XNDftg&PW3G9a2&NSEi?){gW zh+`tU&#x-C?h%gCas!7C$ho;f2GAJh#I>t&pl`{xa8X^QZbAUCc#1ji0Bz0BySpUT z5toqQKn+n&#NwVIynW`3(}^S3vvE}WD0|LmQmJK)>YNeUL}aouROuLBXb!$;b%Yit zL3y$;IibiBxT58EqO^X#ujtHM_@U(`22TI^M4$}hczDiAs9%?XH5ycM%Z@dG)nw_~ zY9>&ZVH`3j1X!9U?Bapje)@;8DE6yt>i?~T`V;s5D5Clo zB>caK99jNk(cc)z!Sr85j&qt)PP@M_@I+18Hp>{YHwkch-5ZgetRwMMurdm18Jd{3 z7mu1)_;tKSj^meQXILbon4&xy*BcchR`_xPIpB)+ZooahrPt1#b8&e7bdLDoHYOJy zh-T?Fj6QAY=GXP|mYcTBBqrU};kWEFM(@+YZ=a1{Ionh-{J!v#YaqeLpa4ECaP)r5 zgj*f=raN8L5$mFLh8S3WT6}Wi=X|El{&=(&_t4XmYw+MXba-CFExCKn;{DF@`p~jA zq_=h8yl~E`63o?{yqukn*bzQu=PK+SY(A5{Wu%&;OS8MnIj?i#5gS{KMddhoTgRv4`JiUTXkZn#mQ_2NVPpi22QC)JBsl6L5woL%B!DH}Jmt*jlxv$&t9dn3f z&OAX|uI|UrH?_}5dnWv6np13#_>dH4Yg{DG`{x=0@m8+ z%YDDfxo0k3xtXs3-ikc2Ws_eqNv$JfaC05PtXSq#OJvWEC#+jGa+YMB<=#(A0D%k> z=cj2V6V$kf4R=_F{b?sbl~@bKAb z=!X!CMo;qggsCFfall<=9eUeDe6og(#l9m|j?7L4w-W$$nml%%ldVi<&_2&}9EV`T8l=(rmyI@f`+el?a{%q4q zY`=~5+neU6my9zov6_CcNeA;jN1%KAsfB7pWT*j5ZQ@(}wC!M6I7WYTvXTIoVB~R- ze)a(Y)bt|>iL{6#37Q?P+kz#5i;)o7gYdFu$cS*u5Odckb5`7NT_XZm`vM+vGb{y; z^6{jAwC|Z;qgaY4w>vPF-lDoWE|GHaV_Un<7GGM{J|7r#V$QFQxGO>kc=|02)z>9Z z3<6Vsj*EE?vFd7}Bx2gN)*g$pp-mdsVfuYyH)#nT`N{rctop&V8VZew65hsv8D zZh^&~)C8d_rO=8?qvO7mo$+Y?aVte_i0I_3cIy?oIm_J>$(xbI{?UI<@c?&0|5wq$ zbJe8E0q+i$ZG$!6!<>-t2MMR+!rLoYJseR@g%pO{@CY12SBF9bwYm^wW=K zH|Q|qEqCk|X!$@?_98ZRNj{)bJ&u0BS_#Qs-vUG3_7fYh+s*q^os|PGpET7GVDd`QOmN&oJ`OZ^+R+)w ztPupUeHLGI&%y)ewR0j&8DXJ}Sb?3PXvK!oH=aj>2m0UG8?gx5 zYv~hlK#3*g0PvvPr8$YGoY-|1bKDaGjhewYnL^O19;jc3@=5~HCBUbg*{e#G!%2cn z-{`RgzNh2MSd#0R{emuc=Qa-mc6J8L`r#W61+!SzjS;Dj($8{1&tNU=rTSguf$$gn zc_Dn$vU3O2y(GmA+uiVy;jd%vJHmYw8}_Ol)J3}@>G0;(M?t##W$_M&F9fci(;CoW zf_DUqeAu`>E-Ge3Vw_2c0*7-Fz{U;}TeVI8Eku(!yZlSW z?$D4l3To+6XAuVjBwnZncyl*KlQiY-A`vFNj|94;44!xi2t0afCAN+Y2=*u!a<-#l ztqFX{!P8e9Nx|snnmPG+i%jI+i*lS;pR*fLhRPY24ys%y7iuKlNXbE0mwsEY>}eAt zcBDL;4f;zHkWS3i8cM7$MOPMm@!GPWnJp2FKk5W7+2T-96e$T7CWOd-mdQhh%-3b2 zE>#e|IUo2gG-+XFtV5|ASW9<%lUzE@VBndqso*Z;NtbzMoT7@uIEpNkYuY38DM%+5 zZQcjjzNSTs-Fh2Wi6-A!`LavX$;=}t~9;h8s4-!Fho>zTDJP~SiwTiqWQb%FXjKIPrpX^XQlBL4a^7%0a0t$#M!(P-u3 z^`eMn@n(-QmDm$BreJWVuA?2ja=@-^;x3<04@Yaj0owQmE}Xq9i=XyH2G{v}8+`k^ zKNNlkhlMv|Y)se~)4iknVt4Xen-wyR{l0PzLvy3)g4-5%RgeKJkYjha-dkWEW^>q< z@*+J|Mocrh10y)$y$a>!RlLw;IL9g2lOT}IOSYQg=9Ndu?ymadu9xZ2dyD^loXJLv zk6UlU5VXe}>1`%#Dt}OUNZ2q^cS+=>{UT3Vza7f1BOEO3&x;x&@=M7L5A<+q741^y zj;AKNog+((Qm34E*W)``N2nnrU*KGSc_S(dp*D66nLZ)jt{0Uo%8+k`}% z(`~mzodotLKhZbqH?d?DZ9&EOH;loNISxSDO`3(u91ycRW9nwwPN=0{wK^8xlf(l5ax=h3{aR!I5$`ue>i zA2y4C&|aJ45%?nu^a%3%an@He4Lg*o)7-n~9oK(X27iX`zx-d8e{>%FE;0NMJrjQ~ z`3>*?SrGig)GoJs?`Td{r;df{E zzZ4s?{;7F>lRq09%l~Wu7i(xFtu`b3Zs_%Xmfa0-xQrfUh%8LjQLQMj9#u(r6@X{z zaCR7G8d>Upx;YZ#5TA%|(BLXo$&V^p-Nd+?e9-F?Tk^oJIE9Hj7o-x0)5y#fjVwjjNXC zufgjmC@u6ud|I|GbDi_wy&<37;@~XluXikL2JPbhe@J@^pt^dlVH+zh#ogWAiQ9-z?{8fmL(f@GURC0?nH%f8D}A7@l3s($f5^+rHL!Dyd_*_ zEL^e4jh;{x`Kia7y`bo6wpvm5U6-Ya4bh3uYKw~QDB;vy*Mv_Qs^#5;Dt4Ud562EK zy7xzl&GI#pxX8seD&uSQmPiArJPESf7ggnw`8-;f;GU2_wSN$2kxLPF%NC0dIM0Qy zO!}6jea;7oVC~WHPsP8le^)nsfaqjm)TozFs*jR=QB(IZeDT4Qxnl4wU}pu_LbRwY zd?!m~r#}1!j3GFyPOY}h8y!y&RPy*tsBpObuBsvx?M5Xkl>Owqb|qbQ*H%}STRIq; zPx~Hca=PDS=z4D0l%sy=sh=jg{#elJtnL7HQ}K`Q3KjfB#^7b%DTRsV9*G2{2@Z*ht|13P^ys8ATkNHRty4;x^6^-nrPV~4%*_2;BX3h8q*=GeZ;Cwxb0BYJ2RbFZHwyP_qVNg6 z_L*dW&aV9mzkv~oz;qRvIbS}ToM}{|tuIt1^00j3r}wW&FsIQos$OaFPwn7U1J~a%%=UPUk~|JTABCa_~T-*qbgJ< zr#YRwzmEK}D0_c>J=D39%UyW?CSF^IY_p5t`&pXVq^rs|t2cyAKbA&C?m-Xp`T`Xx z6idSi10MPsKSR;qL4|i=Gh8AwRhx6$j*b{;KdW5kxTPOExyi$u=s4EiV7W$j&=dD5Rx` zgximx=vZ+w&acYHSV|0zjz5LfF2jBih7=m;swow~F=tEZxqAGu=V*HNV7+(c7xlw@ zD7IK{6=eC`1V5cNcj$Y)2OTwLvP8p+T&L+SUL*d*lj5>2u;HCefxa6tJKV}9Dv>Qx z*VgSD(w;YS8LjfW?qIuFL&|rcakUVc)*f8`v@MC{n%gro7*kO^9-bv%1<+mn++9_s zw2}Nj-TH+kZ4E0SSNz=mVI{MxC zkFa~f{j8hgONs`6-Y0eKT;-p%x4BAAj0b{rcy~xei{rsnND$XL9lWs_Ds||JW-I0C zDY=EmX=jsHTHbvwxa8`!OR`b-`D@7DVkPIj8;Sa<=BeFT=V>BMrUW?6(W<6@)G>U{y2=2)KFSw40F=p+3Ig{+sIo+ z71D8(WV^qFqOl*Ni}mhA^d-mpzk}?5DIdaSuv{!DIsaaTbB1&sPM&;BfSmb|wci`g zh@6bshSz;7!fQ+-vzJm|lpJ3xbi+AhW@C~R^|%Y`OMY3g9a-%&D;|8~Bb;>D2fFEX z_;Zf+a)TmcPfb`}{-jKeyIU(#+0@}{qAN_{eiAn~PiNWm{yEQMZ{4ipr(W6*pB&** zG8i5lx@|NqiUTHhk6}4DihZOXm5PorWvLEyHBjB!X8%pILK$q%9Yq;$~$ zyNx_)5JRZ^swF6oiaxrlx|TYPqbmFPwl$r1LVwi-(xy=3nPwY?V5}x+I{nO4Vvpe) z1*VfUc%xv^_JwB)WA2(?a!@?PQIiO6DJdy5 z1QEI=i1fn2Vm2SA8lHa4O3wyZ+xXXrAG|6IV8u z2o$dJk*GLjx(`+OxiuEvWav(@9_~V7GCfx;{nF*m z?a#1Jz`UC6m5BfKE%HATclLjat9X_DA1Bza^V5Hdt6=+UBc$J7@ci2Z`?dYI&j0fS zn~8|+mF)vJM*8o7`k#~VKUn-fIQt*7?LUGr{&VC7+kZmttQ^e$hlw}VNc?isCBJT$ zR$;FJ2$%sXD_bme)mB7O~TR z{EWP`(N-^eu?|s70aDUdAJN5)i3NIEbeD`Zcq<2Og3q;- zVQk=?4VU&YMltwsoY_BX-#5mt`GVY8eU8iYo9CM<&xoFPHY_jYBYvm{6(#Sy@Sj&T zwZwdM)4M)|Fe=8F6~aaTbi?-5EOp^&EzX4{_JMQmoqO%zH3(jDGBMNsVpbfh6IqhY z%}2(Fd%G#k&z<$AM8*`KYZ(=*B^eu)@Qpr!&83i~QVZ8B8EexF6s?^VN>7gsxbvv} zz;2V;Elt49F~S3($t@w1H}ZJD2kIe#K^a^CrOk^IOr*MLez3U$ix3#GmYYHX;lU^S z8Ol|;NNAn>*OH`evP35J-Dtay6JvjkvP6;J2m8#&i#9Fw?Y2Lo_f5&g4#V*@(33@K^7kYBQ@OAkgupM=A_6ERQ|L(A(O zvrN1=oWjA5DAB)NM41Q46XH-@r!P<%`@f~_dzj}y$1P3{?KsU7xZ`DmvL+L`CPt}^ z&fdIZym%3q+OEDRAq)|hLNA9{;S7DNqJV^}p*?I|5Mi}w7^GfVGvX$ExKaMIat;MK zWeCOIh<4{!w|c~n)*sn$6KL=**^p}ixrJ>iv`rrDm_{TFkDRBddrBLXxH)aIA%kx$ zU`Y~5#$jW*rdTjMmYU+ZdNJNeXm^li~0j5>@H#| zol(VRfn;H=w4azbv5$RU&VV3MthbgUo+z*GAYB(S3-)9J4py@mi=ao@Iu&!1*HPU{ z)Pba)soXxIF8Drb+K2+2X*gQ#?5#RnZ8oQpuXSw8@NRFCgJy3TG_`4`v`>nI-xEXW zwwC?~*Vy`vrMy@^_*$Z(`sVosh5?_gzpn02Sd=fg-fcLg27+H*!;3efU5n9nIwz3= zk4ip`IUZZQ6?*R-RAgC-e34jO`=$Paiez*9m;9@0v)Ja&Dttn^^mS?9qm`Y+h_TRy zS-K)$Yy7E}LP8E<`x%dJRfO55oLC=7WRZ*EclTE`B`<39;WYbTS}l%WAq| z@ZnjhJQwUdaS&lvIv{DB`D&Wqd1SoIKxA!=J?5aL25B$nvR<1f5p8f*dGmCdEImCU z)nZT6)k`V5nn9||{aD_HxH@ni`0z!LL2Z0x_!fUZdP-j6lvXluUR`qCyP2RH9z(kZ ziCkJK<*Jrn5DJ7uNT;iC9Cv|)K|Q#>gAuel6|Wzj$p<~C#_-_MIYY>juVL&Y3yLEp z8*l5bP8kAUIR6Ry^LsOl)P1>w_A#rww{rQDAxgKhAJRki+ydef6QkHfJ+3T$9ADEHT)ME*CvJq3w~X9FVac<4p=(?J=%H;dnhye@mRO0Wjm78b?Bx#^wsO4}LV||C zH#w2(@C|O@YRh+SGKN9ReJADntq9a_6eAi_7Ox?!&nd!};e>`!73`EqSr5mx_Bg@* zqhf;7198-C@Q8n7*!zLpI={us24sZv%H2-Zn0o_ij-k)2tSR7&!j3Hwx%TYW4w-?R z5h@4tSPBh4Mx+gTRmI`;E7)Yzv^4LJOL1&#hMn4UgVZ;3!HNUXdhWhBZ-!QXe1mTD z-mrex)n466Yc@jxWVZLBKDohsP!4k zf{J?kr4y9cw|l=?jhXRbf+|GQL^^wSm_e+M6$V?M(4#iw2>G8klZ42G9B+$fTgrWW`#XFbf&0S+6b7yr!mE zubESW7s+-Sd#6;i4yu%-qRg?g!mOh^Wu}AqXt<&6*|g^@u{e}+h?y`$RWy6SK4T+* zn;hcYC^y%Q;c}{cH$?wBS|vF%cN3MLw~XO1RzOv?9qYjol&sk0#k*mV*j+Q9N+$H7 zx~~AlG4Jr=vxdRN9NgqL-reYVS`|%CF~^j7H}rL9Qe&*pz3w-H;FWbP1$}dtn9wvc z$HpWavDz(e00sjhxIJiL}@O!t9*vL|h!KL|lNJm5GUn z6)@Rm2ju^nxB&LC0p$VhoPc&<0?GjUasv`4pp6BPfI435fimm>-LLsy^=yD^U;?%S zd&Aa>{C`pZT@6b%Q!HV#0}$&Y_#17ad ziIbg$hy##w0m{HWz`j7)R~>+Qpl@F96W9hw+`r|3`u`pqIPUNJ{cRJl4(O*>U;TXz zZ2MjQEe9+E{RQ;T>vI6+f6K7}#s~W8^;rP?4V2~hTPGXfIRV-Lo5%|3I~u=kB+LHy z`U_YPHg+_0Ft>BEbpSjmKsfuK`_lq6{3E^{_}={Etzi^4H!(2=>?sA@=2i=qnT65B z9QM!6a@qcjxBust@56tc3Bbz14tTr%w=)4M)!glr7MeSRlHW!nOXXkN+d5P)!$ZU; zl2b3Gr@WQc_*C0P#~jhs{}5v2ot zMvH(?@QrCj_S$pV1F&e7Ml+8DU1=P~9;PxGgPdku(_Urcnz&a7o0?{7mNS{Xma52m z8*|k+4@G45#at~+jrk_VTMy*q;pVltyW!@|yUo*OE14R?5?hKA#v)a0_8DB1Y90{p zxh`P_X&qHbZHcvFbq1ltlXJQ9LIR132EByC=mar{sjg7!Fx*i!V#tVbV{?dc<75U+ zZq^0F;^VBvVdFnpiMbG&56a#=+ZZ7|i1O4SEyX;BIYW1g*f?M~aM&R_%1+!LW-^@l zH>qvOUaf83-=AN2JF1QGk>3g0`F?bLjCZ?Bye$od8NVOdZ-iAKZqWM~FxdSeB36Wt zhj?Yo2ok*U4C-*7XH$yvTL9h={@ul6QH%DXEF)@H+asAtm}v>5BI#jJSV<<1!34fvG*yZEX%H&uLA5&fD9^f%-!&2p+KUdk{?uwcL+--t>Zi5i z-^&0K`yP%GsHqq;-?F=D*HmU@O+FmIZUdz@+i5cddQ$Fm?xE^>mDAAA=A%1 zpXg1iYJdZmCSHjcE@?u4HHH7MaI8n0bWA@;-Jam+tgpTPvxtIPaEq!o33aJemG$v~ z`FUTkN|%^fEcly*i=#ITeB#FfEUi8_<@;lVsI4LjeEklK zhobq~#7(8Q-o|im_jI7p{l?~v{pk0%mlJLhDKa+ek5WHtCv0_EIQ)Ge|K z1mhvRcF$B&FNdplo9#?Z`4I4qgfrAYt`c^(B(qZh#)$^yAj%=2 z>+^&AJx-eq>Sy^c{x=|6>mYA_g>MIvd46j>A>|=LB?R?>%BA8Y4(c!+BUV6Mm75{i zR|s7dfuS;`gx1SJI^;~Yn2~V^AanNXRXH(4z_9#+ejcFZmI$RzSE2@GacR!l@d?5K zJg%GU$0&uOsgOpGYD^!B)23Ztnmn!-V#HNXXUR;)(U=kZuKmYs(Q+Srr7KGVm_x{_ zsTnjZQrRrxrJ3zF{);m~@w&8)@(x0xrSxMZS-~e=!PmOwnuJd!2|r9>p2E#8OgiX= zVKOlq`(k(yRNExP0*th^B|(|<)B30EPJ!7L|lX1k+~mSrq5t^qc0VnnC(|hgqZ9bcxIvau*cw@@>-ArB+J4IeoRnLGr8mXK*}#52}q7S?^$ud zoMS)snXovdNkts?k(6VqQ_q%LS5VLIf~;8WMGe8?l414e@39D&B)9P-ds&-Nu{dOk zUq+YoEK08vJ-e@8EOm1b*0K`O%oMk`gD-zrv*wxs-_C#vABl^*Oju0(p^~*!Xy6V< zI4bBLtb;e0;}=h>&Gmf1j=Wp1zEtdt(Twe@(4&;fzL%~Z&XEO*)8IdwhHzJRi2hE7 zpGqmOI`g|TcK>?nMZBi9 z?a8ftyl`nEa~i!9u!s>-HCV82Tk}}Q@!V?qXF3dR+1Zrw>9oeXC2U4^p1<%>4K<#q zBV#p2A7eGzuN9A~H@f9s7I$X9T~mC~DCkSLsX#3@JdBSUn&RLpz6E1O=fmd<=N=Du zl8JfZ`@phyrJtPF_kO88IB(vnw~-a+V~o)EwEL{>NizWpYciLHFtTs64KGB9DR{Ip zK@wdVn~VtEzQ*{S9bgCw3>Cr7`G$79ei33<(YC(+#K|nU$ueZ#H!|OQ=}zg>J^6aZ z3$j7Gk>4bupsOOaW#XewA7!E^U<6^Mr1SM?G+H+kAKRHH z++a`RV>r!iTO+dy@kKH+aU<_y5QjxNZCO!6Uo-d>ukPBfgO6|3YNnM6W!zmkzh<<~ z*p1EM@|knQrdJmaw1rOw50G!vJpO=Sjt1_fVlbz6 zC~y?UMg7Q9f6*Q7cyYLHSfFQn`t^gf&?&zVDZ544x! zW16}(^l!V;cFc>#wP23e52F3c*4CeGNsTS1nUbd0FwBqOGARP~1NJq?VOCBPY5k%x zop&l|43cr#Z1hZ9yb}=4?L`Q*dxZo))66U!S(to-Xm$KzKQBi4xoY*^hdhG*K<8TtnY)ot-AbZ3zE(C37#O4>744IDnvSm~gYR3za zPs*!h=pQxj;SOh)TwcNmr5L`0EDOId!qi#YK391?raue134Arw9a{4;=vAIh@mEPK zWi5x+_~=a&ZgE<;Y*`zGe^*#-%<@U36S-(q-CX(YaT;>A0_doy@}VLmPI}gRN?G^l zo)o;(o7jRm0+egd@g8?1_1MEJiyKLV+ThC{>T^tSCBrI&(4JsExZ~rkUrr8!(xz)` zPK4-MOxrnI!0(|VF3!gfm|F)VM}{SPR2V8l`SJy<+nUa7g}R9d&)X4$t)x6a8G5ig zgM>ALy%z|i2IrnHt9-29IS|o*$=pOgC*i=8O~KF;GLfUyW|#8#fqrJ2YAQ^wnZ=K( zRQn@kUgA4x7TN*PHxKbi2`-$)=Q1evB;(HguUrFqEW|(SF(dV3!S}n9?98Q^69g8^ zSl<@2-{*BxZfF`;kn&fcJGU@+dpw^vIAuJ$VwCquHZPVv!5&1O4IMO)M2v0K9Y1XI?m0GMy`;Q#gU#QT>wpk(F1=MTB}%=2}?d=8>my zX=xgA^`&?|ORcecnKNB|zp`m8*oBM8q9(fY&5Qq1446n*3G47tA!{G*f!PV}`VN}u zj;c>=sGJ3hcl>!O4E>lDMW!+o<7$vVGY|64N-JDr_TF^FA(ujX936W;O)&}94OD)? z(m*2%%o{1zADU$=7AuUl%$dq?-x0QlbxH~!*-d1h*FH0&`+NfXfc9N;yKG?(%e6?G z>W(v&3%+^Zr!CLD12cu|N{7^p%<1%5-c+R)6%(V}6r=m671q{L_O?mz?Rp2wn(JMl z9m0f=#tPbUkQYno=Xm-^JQIZTpGy+l6g9UUU%5@3jIv&Ou$xv0Xi1aOl(8>RjK@Fa z7&^n6o}V002A#i?eI(R1OWT+xdjgNEgpuc%Ep}RnXiyA>ku#9J7!{Pkxu`g54S+w% zH7RsGTI{({=|=y`2|3kuwf&%q$-;QlF&)gHwzDZx(Pc@MQZ`cN^0u#fzIZ>^l43+I zhYJJmq|32&Rb?RCj>r3*yTisXoIecL%Z$3mIA)X@=loz{9V~*e-VqvO(7N3^)^gB{8Jjpi`VOX-uTU>e74A}?mn%hIOn&D3Zhe8XL$%~2Xs4Y{ zN*`$`$=``uL90~29~4ttslba7##i!ovp9%v2XEoKxcrIO z&FKWz&K3Mj`jNXY+}Zu^tZeJ)WMjCF_vpkdlPFWiAGb=@zhITuDkmygwv4H6aC^z$ z{INgH7Up=AaDlbhjr{vb7H@~Hpqxu^dWQW^Vq=f@-!}B@eN;dxkU4LT!;}(($GmgO zO4D@$T?>)WqFxSvy!b3)lvh*hzU6r1t%TP^z<_D}-RZ)czRcLV*3&Q$)%J%oUW`Gu zs5~A?;q&Kon-kXx=nx$b$#RwqHVOkJcg+T@@=8ZG$*?^Ga(RPVbP+N5$ZRZZY_xZe zCkyg#1$8Rc%EtCQ@O=aoi%a@~#1vVdSy5b!l(A7}r5DdK}qtr97DxE0~e(A0zw% z9%3tk{BPjsbp;Igg%Pm$6cZH{7pFC}b}|QW4}c)B_H`xv`v=H>P?UeHm0=m*I~oGd zbm0K*M+~fgJ>Ugc_@HkmZftI92592~#Nes_U0`DcKo+#nLAt75gA}xAA=pj7|pq`nT>6JVHkA|_* zH+3Z9_^TgaDQyfu0sk7Y_$QY7$L;)2Q0VWkr2c?H0Q3Jd6yjj#AmRW(2Vmj`#9IOr zD`4j&R>04(>>Mormbd^|;T6DevJr6s~+D-_^ZZ`uA9`mH<9KV+SPQHPBKZZh5s5Fb*dW3o!v~ z02~g)#RWh#0E2<{aRCwsCkNmTIDn0S4r~DC#liiWa{+$d2D*lm3t%Y6UrL#ofmQ>x z0}=<j^cVX4$I(ITf9^Z*Kf@trMiY~Nf5XqpY->&G*ZKYU;`7ZA)p{Z4vKupWWo-pCbhIh506(i? zn(_e9crOTPLHW1P5n*qI(YAhxiG6mekC3XgF4pv{pPxydGN*4t65z>V)LS;^HGOuJ zjd#3i&+zPcXaW3jvh5xSPO(+0Z?=)xZKK3kGpDb9tX|3)Mdp^olZi0I6aGko=!iDZ zgt+b(A<9c}y({i+Dk*4oF_Yx4!WgjkhU;oNZHh5BFOgJ+mqDwo?^tCb*q&Euo3w`% zWtD=_t8dJ5JX6DFjamOPZh5xW#^4SU78!IF&?3A_A4*e7 z_0goF3MdrMW~GnhX03)pHHGOvdzF&UDq3JSWNJ#>E0mYaB?>fzXiB}2KQ8$>3rQ0T zo6n)R4NZ`kXpu`WeSFA>--?HWmn|_b-tL-t>?U$VTA6~7R5Vz{O7U5)UC?J_XzDO_ zMj&f}*R@&NVaN`D7w6qFV?YWno-IM)>Xq`Z{$8qJm5KgDG%?+6hno*${aRZ|zP?=&^-XPX-1S&jzHaTj5&{D`|R#SG{gO=88EbeWC% zd7|VSSeYbae$+D4cLRqqt#tJQBbkn-vLwLR|#bqHsans@n-~2R7!`rb>QP zT|}WtNehyo!^Z20PRYqWZvS}~(Vo?e!tB3s=CMYUKfp*~v>kt=W z84YsA058JUpqEORaBI}DGp(t}7E!Bo?p$tT>!*9ECMf3hORkLAFRoQ89WVY;=`u-^ zcUK%1J2*B7@_rJqAByY&pE9%HL@;z+#OcN9^kq+*_f|DSHQ0}>lX$o3WejgT81TS3 ztT6PYu>?1zGvyA5_CUC4;2{){TYK|Ylr?skR!C;{bn>K1>n(#0xzV)xJ-s=3*@jj< z@uwbr!j1E%3=)-@qZp;BRT93m=L_t3m|iB~+RK)*D_XK5EaY{b^hd|#b$^kpT`-%Nmu%lq^tw_6kRF^$G(eDVl> ziM!PzVHTz1at94DCg(i(u&6Qv$ru(BJZ6ty#w%X=bG1Y=;iyFG z!<{5Jjr3-G97EjB*oo+O2gmpChd|rw>v#0L+urR|wrJQoZrp#gJy-KVV%A}#+Mo>B z3yfg%0wX2&&MOQPkc}_eurd#YKZxI&&uKszQn!#Cc4zo9M@}fgxaKM0HhfVr$j*qV7;H2((Yyf74uyR({#wHBL5A zHXM4N);$cStWe(Tgvo2IYB5U@_46b}kp0P9pt>nxnI_t-Xt@(T+3$;RBnahKE5>12 zPOO`q%a#V-6xsa?bqJ*iC(KT0Ed8_jeQbEhw)c~Vbu3#+N6GabQp;VAuFQs-G2WPl z+$I+X)x|wz7Zza6X@LFl8$LCe7E)7cEKRl) z$oe)%L)AWP9qcFSHY3Ryr^g*bX7KjJz*45}SV%Ct$k&{Dj)os@$R9=3 z=lMG8aKUA(65M^Rvq0;ggpou+`2&;S&&bLHA^4$a;XZUE5m3?6H`}NM>QI~UHqwG% zqn=vG7cZyeWK=J}xpBLse4^Ql(O3j$)Ar2{jxVL66;M}#`B1Yj{)4}elj@6-JcS(m z@fvAJRw$&&yM*ij*c;fi;QL@tq@mN&aK5ZS_r7a-(-*9r^+pty>0u}$!d}ng4Xx>0 z?yg6L1Cf)1c6SM7o)`CD@mJ`XV!66@b$TFB*x9!cGy9W|1irt<`^qUT;`g2?A745g z_e=>MDN*RIBh=pxz1XJQos$tw7vY`!V6jUILwGv?#`(m2)BVLHdZ6brOl+nT&ScdW z{)4+!uci7sJpUP;d>oa^?$UJDB#tx2O5SZmxVcR#CUtIH4u8t#YUf#y zkG=7N(}QdTAuTJ}i&2&!9e>1DaY)%LV!?9uNntNioU9j4pC^p1rqa>z_;`PYIS7U_ zv*kvbqaq(U40($pLcR%U78ih-@e_!ciiBpF!-gCzuYf*eyG<1#acKC%aj8QglhJe5FGlHWzDP|gJ0dR`N{+}o*G|GE zwKWN)D=SLUC@7hg)mL)_;z;#;5Hz9;QJ4-aBJaytvc=35i_@-v#oit~Eu(YSFKK=WNI4nqx>lK-6NjOF-Da)gEY}=IV`=YAF)tC(#Xm6%Eu9uh6xi+ zP7oGlRASBus=_m;=4P=7G@);2LPjG;4x1yTbKU|Qk(ssi%XY=ntX!2WhiR^krlhux zf0Z*ni3<{{Y3&P`WpBXUzvGO*qds66rQS=aX@~)EhJo83IO9LB|NlSZ3;<;KFPy;! zpcjAQ45r^OgP94yFo0Y`9I)d2YZ>_$&G=(o`8Uk~ECc_h8Gto1Adccc(~Q@};-6s& z{~ovlT&w<1G~@42JpZ5>{{%At_w_r%g$ob>&Iw#R16J`IfRQ-?+v@?B{J^?Db9PPu zCIBwHfw%(D2bfqmSpWbBu-N7Xpn-o$Ks{`%fJh8L{ckUUdN50EwB23DEd=ft{P{ z^=5$v1C98d|Goxx20H6^{@ba*@~iOxC$j^N9Rpqi9seo^&;`ta^?x~v<2C8icnBcM z3J@|w#LUWQVg*1tjP}m9PR2$CRt~S|kd^zdioZ(CEUb)x|9O2!Ge9p6MkjMCBjbPO zDS!S;{C7NsgNq$V3I7+K(%=rIth{h@i{GEdtKP3(wfoVrTC|`G#j~plE2_<)D*##? zIb;o6uY}dnMDM^E)*5S49^wrg4USKz-BbsW zPWn5R9sQ7o@8~DfsSH|B@j!G0JmV+t@v){d&X@K#VsC@IVQK{@tq;piTi#LnaCK1= zG0`jzjPN=Ejxa&|K*_@+OZcsGopo_wRffZHTd4to*+pi_Y)ND5N1Loa5 zf{v*cGK*7|X_%>9Q?Zz-xGd{Oy=8Yga$n91Al9;o^tN_z998$ljr{kbo_JM_moi7G@NZyr%f|);rus z*f)R#@-2Ay`(C{O3M>QYE|hcRo^a&ehhC~~e=M0u;(la8lewE{%_kuZ*|6 z!97vAY5c~z3g>ZsguJ?)kv-z!vux>Ao%m5#vaM*sW8zrnDcUyYz4z9`yW^Hh-E|+E zr}nds&%Wq=igB~2pi$tINYVs%W4TT5#{zHRpR+#-oZ%$v$vIkb z@u^L;eTRS)zc1SOq+*1ISLXraPI(Ml#-x!%Y-*rler1Hncjp}tiQc(L*v4)M^q&y zWm^7apT5M7Y`=$tIv0%Tz{%`|++*}Bi~J%S3#a1lj8_&E8$fb3$BAHCm;Syf7(rU* zQ)E$r4(XKuDudBy&!)47qS;5stqAg#{pTbx2CMyhxX)pbe=Qm>N7^zBE zJXAi-&e%n=h;lOecG8EX+Ws+wVfrCRt4v$I`%6D;5n{15t?l@%ZSU(;;o?U$awU|? zmx6l9MBuX$33TEI^Y052UpgW71yDO(Bkigbpo<>Q6Dc$jaKuKsM*AX`dWCZpr%S&n z>x`cs%X=}@Pw8M_W1yKqmvdlZvC(OBZgeoGAU^f18=P28`5sD_iC}+T=fHX+@D*m0 zK2fv?+}a6-_h{GS0NQohZd;!VPEya6gA>YZ=5SQmS~IgCArP*`-RMAmHX~a(%ma^b zI6pgQRVR7C=%7ArC4*j{Z0!q%MXAdhFH^q6nwou_fe09B%`zGh7-jOQm58|FSkmtj z1yReBbY$;WHAGBXsd-dHyH+g)2#jOeXaXTlB&612{<;rUo;qZLVK`aHG6|S zayf+q)e$wUa{TydP=p8S1_S5{-@yutB>RydZpj&NhfU)ON+3hRbGw4>1%WOnie|yB zpI<^ziMVTgljh*l0cVQ@99x(IRq3)iUPL`tR?mR8T>T+F$tJ*Z8N$6r5PD=&vkU>9 zD`7fsBWlr=Hcu!FYofd&p)4U+J6yKx~?jGSn>geMUeznWZ z8bh%nrpYPEQsk<3Cv2aj@MOUkjCp#4{nHKM1@VF+>;eos z5$KExG!;lTWGlVFjzX8aW4ik7+4vfwZ5VA8ZAjrZWh+HNp>NVB7sV`20yC zHV#k^Jmm(f{l1hk)w1xI2a<@!r?w*Av6>YvYP;MB88<^j2Y-;m_m%zN&@JOt98XRt zy}ANFEt4eTSx$lg8cvg|T9<-g9m>G6j;G7!lRgsQJi=WM9vmU$21<><>3d7+)q!{8 z;Z&+^caOaCi1h&$8NE_giEd_o8zh4E)@j{b?TI6HBj;Rxbpicmzh5j- z^M2tuh7j?|TL|$1k!vyDqny&E*!1gCupmWBulw!D~bL{HRi%5?gO`gP3%fKLFHy1!t5GU zBjB49PgtR{&n)kB`cXebG9_S$mOc(nqeBI|mu}T$G!J>-*NI-U6|W3=LF<1%OIaPHfLvcav5 zl3d{!^As^dKjDd-Zl9Wgiv9CSM+8LZEjzbI=ay3+tqM> zldilFFqYgwp+y&~o^CwO7I?3` zEoeU?!EQ4g8Gt*~XH04DX4yPrtj^Tj!_%LN4OP~T_S{wYwYB?MYJtJZL38)0&IPH( zjnp{$Tm1|kd1)m7PUEL+PS=?VbGV7smY;|B5px_J2(E;<_i?^NL(5$zu;i`#bv#Cf z9@b15)Z>bC#`7~pwIR8=sRy}9l{QFvgo|*9Alr$Zn&tgMDm%r1aO+c7=7}?8jrt9D7P5$&=dS2VkH3rf2vDq)n`~A<(N2!_IHd zy@Cjw9$ce6#v-!dbRR>5r|@qQIo0be_%q@ml*?pgN56UCe_x=TXnuU3A5t+2j~_ve zTlaG^@8hHiK1IE3C}%0B)vCSjV@THhgW25jix{lCb|5ePBT0cDVVH=%Pdbg#_@K&g zsxqRx%e$m=SZK{Eg2_S)SY%uA-2J^BMz5@6L8nsI6g*{ zl`!uolpfqWkOel~husGWv--G*1u$cNCFtp>%tTDk2=p&JUbh?q8*vw%S}{<*D26bC zJyUk%PNML5F(wXXo#*Gy!4rfBJS&V_o;fXy)?1nw{&8p59zUC$;nX?T=UP6qDyoOs zTlM)`Lf8%BH{~f0>h=#DF^xoSG3X}w-l15Ibo7*!-)QC5wIVObKhWQkEx7Wi!AA7- z&Y-t%FAg!OtpuABGUMqlg@vFy6m3;GHJkbWOqP`!IETKce4J4B_Bdmo4|DbjBaLN# zjuyo2ezKP>6dcG4c;rnG$m;~e#|MZ zvbPRK#^x$$c`V4?Zc;lxc`+4%1)R$2TqORO1I5afNAIrst}*8$;Z=Sf%;_TecOl~rWLc_!zPn`wvw+_v9$dRbx1Uws&}V=bC? zU#bdDzuuY7SX?zw9x}?;@f3&Utrf0idEYSVSIu`*Ds6^c`m6(lv$0IOw`ci5k=KRV z6k_GH!#jHEDm2qhD{nhMTTW=03A(p@bUY=S1*)DL4)TkaP!P z_(s`6Sd_VRCdjCSa3obfBi4o5oW!TlR60CeQ4_=Ik?>G72Y>L34*x+Bl8L#JFLr*| zng|2B#Pk~%O3N}3))1i5ouau2TShxdH8TCqbbSTyJ1$s`PZYJ>J^3&+IlIoO52E|K zCCrHTV4>LFG!v_FLCOTl+QB@6$c8LyIN|aYu89Tu_;Jg>Y4()M{$N5QDe}f_@NSBW zFx_sNkZQo)w%pJSWrQ5Nw}Mp+{?xba>1ye~7P%)c)K>oG!AkW#_#4`97Qe!=WWmMj zXAp>()1&$L5IFU9iz6=zi>k*K{09WmA#WnKO}htXf7E5{+fGl!9uVMkE^uE+vC54*o-(_A}VpG#MN>lx<+#>-|(kK-65j_$!2e!E3NC~ z8+Ga1M3u>{)8P);6*XGGRJ z<Y=_RJuXp&i<4m zp7{Us-{)R^hS_^&_Ixw5XU|$|-nG`Orl3~sxDK;#^Gm+PpGS*2r<7XwzPX>Col`lr zt4E*pjqLW2DCTbECjTzp=jCo?Qx}dB3R{x0DkJeE@%*$+>Lw|7@<+)N*E9n)Oq7Uv z-YMR`*g*=*X3E5n)8D!)_^b=>S=^pNWs~s1xeA1D{@&x?u|D@Ko#18d*kd#HpTF+e zxQ_isI9@rS!MvEhzP?_POUJa|qW@h=@4e9dE5&(xsnl$axnIf$n+RTFb?lzF*+)NJ z@VNcp3a#V{2jPa#Z2HVlyn5=0R6Snoqt1QV%NK|7AC|WrB#EY!2K7>AW$ay>m72xrT0Y8o@!YL{)vg%RtM}QBaSH zzN*f7Np?zEG~53zUc~`!Rtf#3L=&FlZ-`b%lP`LNV#B?H^_}p{xMbck)JU{8yl`hu z7*q_c`#@d%A|Vv*F#S4`4}Q+okaPTs?^jDPUcNznL>a;$gZRX<6~#_i4fF9E^Obrt zv;^`M+~6P()=lwz=N_k{+zjPV8Rav$&gOOA<(_3Ly6ZVM zC99M^CxNO@ZsIR#zmzvVc+pA9NXtk~ZL`tHvLrZk&EuxS0&7oD0#DFe+IiVz>7HZ$ zy`_2jFmbe{x1sn2zM+ebQ);8xh1)Zgk+&+SV2bY;c%-=Kw#F9UjgJvStS}g zzj)qhL?7`PbBE=zstOHP>%Yyf9fgC+FuIN|^U_jBl4{A1u6=F+7>F<*nH|X*eG|nPUE# zm5K`QHr8OfD@EDu&Y_7H8tj`Fi49FW2R;njvV2Sp{r-(>t>s`-*tIbuwzB(?kA(DI z_wZe`r=j+OM~Zcs7`^#56@v=z=_#iQwV(KFKaF;Z{&FJ3@U2`<^n5yR=r+a>#Yp2t zGIAX2>X-@j%7`3hj*lfQ^X4w^X6*O-1aE+OB8cF~g7SJqub+zyd$33)8yi=LTR^BL z(skp~76$%SJ4f%fska|BEBj3f$!Bq|r!$Z}2*0+JhvoX9!Vt0>s`;6}YkAz*w4lVF zOPxw9p5PT1ZrB$u^S7;CW>zv3x>bTB!0VVrT_y4C=?P=RlWm=tJd!SF&t%b^lZ~XE z)t(Tg8DpPrs@3?EkGEiMh}`#vysnF1j#csuBoHFrT8WIs}xbX_urIPO#53*a%&e7}A6w~10zJiHbziPM>YZx)h*j1*QE zmN>PA9(&_Zq)hTW=&ZJFlSS3Afj zyG5*JN_{vl_ z#V_hH6PDnaiZJWNWNl&@EIfXEn?1xfJ9y7_V_9$-K`+dGhHHr8i+}C)G(t~d%nuGrOaxeUxCqUK--}7~?ql!2BKojG)GThKts8PsjNpg> z(;8?b&NXMqH`*b2yuswaKGFcr6X8_HD+wFJFXZdTig*!jnu-|Cx$#8`QQ*E`b9rA@ zHE)b6xbyL@SLPrLmbTlrCm3JE(K6KA)K z;Fv-Lh4S1%8w%PrZBvchqTrSX$0p~>MXnpT$8OSQUs_+eB8sj^Of}E^-g*zlc4@om zo_Oy^?LN)cm(RQqR#2XcE(bmJ4|r2m!~3kLvzlF=sbjbLV0(K6Im3E7Z8*Q~&ezJVvt2Wp%a*=Iyo1*q*Yl^f+dQz3njr#iD4v)k+x&RK~A+g;Ecp(k0s( z#Y`jB%iJQ{&6pAy7kh+))uY?j5s0#1w8le|Su!)}FV`N`=1qb{j9N0nJWHgoc!;%d zXSDju(5@nJl972)nhFBZy`kRpEMHD`^uWI_kjG32t^9{-|f!rDi&= zyQMteZjqg!RMGNH$wGtWV0#Eg~HLA=SuKO9tbcwB9$Q`<2>q8`+U*e zdN_#}W2d=c>M}o9L|b1v&bM-$!YM@atIfKl@l7-7CbLGi_l_FI$=}aLS&fl-zhNUZ zWqp1tFb>0H*{CwQ68TFI=eQ3igTjqWzy7|U%hZxf8!sF@F{xRV?B6WNB-UyU;5EILnW!`|Fu5j*lo6A=zIkRp5kq4!y7{bw$Yiph!w z=TWE6hmOv#@1I8|;$G4YJbzcSU-x7|u!ku0ej2fD3PU!}+?}GZY6t8HHx^j0iqnu;J*|2{ug2yv+EiJn4K*APh9CKq;vsH;j!}T38 zrD~G(#+Qwo$B!mN zS1=#9V)lvUuJwS^yOTvTn?=u}K;}%7o{(lfxAl$V>gaK>SB|9D;=bJkAM@--9tT1IQO3umBE+;BpAr;A~~! zf*3%>Fo3oK*q94I-z?x72CNf+y#Ooll)=sc&{_yahXE88z~E3mJ8%HO&IAH*zQ6|Q zy+dgb6c6;?IUxOYX2>xDNCVClU`zl724z`+COQXj83&=cX9FB5{)sOCTWI_&5)FsO z0jhi&L&z~4!G=TIKK*PS7_vln$&4 zIDwS_Com4+U;^cU{R0Ou2!O_b3poCO>SG5mI|nClX9nX0sRlq0 zJLo?f=sSE&fw2KJo~)oPY|!|DJQgm<#sG{nG`8S3*tnqffosq{HUQs4;|3pRP6+OY zoJxRt!ML)4cL295;Qa9(!I+#y$AQa>zq1bjKmNP(2>7fz`%V3`^9W`}Jxg0tJyK>i zMngDG&dkC1&X%3(H6k$tSpSS4rZYHjJ8JhW;TXkaxxm&Sb+i@%#03}5Sq@x#%QZ&?`WoH zX$ag|{06Cm0s7_p;-4UOc24MH{%?@_Gj#}3uS~K_u+#4}y_GtZ;EF+M9*nPrX^=#1 zzJwUs5P%ptPV~HyiK%8~vF02rlcRz{@Z)?=1<*r$wt&LY;CMR z-g~e*R;62nf#8}*#yWz$o-`E;u?Y*81t(?Z z{Pm?9Z8X=-5ekv!YG5`iqelLs={`%HxlhaIWB5ooZTepM+evQ>AP%`Hp-g|wj5bW4 z&y4P^n`yS98I4NRQ995ceOg^z?h>GSy|pGvCxPK=dS=+g@?54m3H7H8kG1YUk72ki z(MH=YB`MuQEBPcNHm!GJ%b0@t0rh^i88xD?BUksw`O5ZBd1%iBDr= z5}!2aGEwohPcl=6PP0<7GJ}I#460k)6Zf&dSj|bzA+?CLRE5%qUT-0GCR1k86c#~z zZk*w7W;FcVIm36+u=2TeM&P1Rs0nY&^Lo-3{4I_xXe|z*^LP%pn|O?@I!1Ix)m`e{ z>J92`>doq15-Gc*cUI%xf0kLT4l7E0^6t6hYKJZ>VW;?WaWSxc->%tgjyKkv)WR?? zGv&@_{NeemL3^wAWd2-|hvCHMMsb&j5F(dNW-dnWU0wHSzap#+l6n4d4!hlV;m<>< z)a9S=(H*~(M5P>y*FG^EW?%|JMsH_gr|Mj)MsZ^-%Hr#7sM}o`%Qj^>+D3bLkggm; zqFs(hR4!zRQgVG~G86yc$Qqp~OBSir`+Am?U$ZN5xnq+1xd)1{zR-%n`V zD>VfL_ut(sOQ>^g(7%g){B}?OzI>N&%)rcc{&9@XA+aDcDq=>BpkQzpcd^f8VoV_N zeRE^yq@$vh@3eFanX2o3SPvW&=pJ4SaT7S^AaoCOrNP-tF@fcxaEL zst3*c{UsB%tt1*m@h4Rxd7}xuzmb0k(W6WdIH!!Hu0jS3D4JC7BXC3xexExe`gSq> z9iE7Jhjn#I8t*;f7~>e5UW146-HR;o7DVqVbhj(ysz2V1VN}U5+mN`M!J_;uJNCBo zqEtm(O)wF6?r6@XgA3s)A%&mEzAx*U6z2>LjUpwx_fKc>eC%rv`?!iq^o|E%-wkEU zi2HI-`IV?4%0$JSWjx@7}Qa%IU?o zaxK(1&S{emd={?6imdwQNAf<;3PtNiV+E%hVplW0pPsl%hjgP{^?bB>V4F3$uUIm( zO8vHk=N+oh=WT{cIhSX|$=+(`tE%bF@;`V#R|+G%bNf=VcgKpb*wME<2Tkl@qn!;w zM77zXM`G1+1`Zcuy9_^$NIX+hPT#eAkn#e5ppAE*_;7;|6l~#%~oUf6T5-ljHl8YS-4m zq^RK>L)ZE-QWD0-rc^GM`H4o(Frm_MO;|ppgn?7=HIn$|^0T}35oJdKqnKQ_wC|g< zyjLH8JV!Ea%)DqWktl2=(Gv2k{;re>m+|w1CVEu41;2)lTSKQ_Z^!w6Fd@Qv{#VRoXBf*=KD3yF~zT)^LXqBPr4WC)>`m z0UIy$)_#ppZ$?5DqTALt6?a6iy)0T9ED)BNF7uB#+}RRS9cs)CGZZ$~?`C-TCGj@b zg$Wep&89-S&LtlC<|GqEY2?_oz~P*B!|1O;ccLw_Qi9Bb+2hzKZ-(p8i(_32CvLs^ zWnfUi?6Ol5rCMSu{X}kepMhA)wz^flF&Q1U_=mY&qD`W0k}r*%W44$cdqJO%I48vE zs9xUKELbQoqmE=voDBMO*h``Ed; z;^{h#iv5fgmTdEO-}?ow**5o!B*Bp5O~jp?r0SLPxkw*&8ZP_Qjja?kK6Dsy>1-1m z7!>hnwR($a`7XAvI~-&5o>}`K!b?Za7mt&W8Zj;BmO~5Jy?Ep`sxuTKvbF2Y@%SUU zJ|g0bye=n7eq>WeIWio7MQz8HZ@W)`uoY<|v0Y!UNPXqQ=Pf_2L4^{;j_ZsXRiV#o z*3cs(HmB?P=3;>{*w{5sWxMinL2YgAt2e~s8~biIgop$=VyrMvyO_^nt13Kg4 zn}V%^kMXmNrmk2|1hHHhUHFcMpI?-Gmlgo7k8VTiw=rlr%PkGHLOz{mlnUTx98htjcH;PrnX6?0KT}($oz7(~_ zjlAMBqRUMeFragc`Y_-QCl}_8$lGJ2uEbe4*c`T5Hg3|^HCYw+4sr)?wlcmY*N?}g z_c$L$?tn6(?cFl@xQUwcLUD}BbFgsCmB8pkc#qDrHH63(rusi{WqQJ1j@`azoHJ}PqW|{5B~$g{xnXru;+0) z%Oyjv>F!))FwmkWj>FNpahrm;9oDn}6PiU#QCwJ1qn7X!SkgT~aEuO8HsA=g_JjTH?|zO5$*^;#XK!yx^;%pGKff zPGwYvnD&&0>vp;cF4GgI1o=mr#!ovVXQJZ!abj;x+N8AdOg+s}gncJAj9R^axU--J zBOKu~kb9wXsa^6JvU-iN!?Q5sLwj!J*hLxkz8BnW_$mrsQ>zYIyqs$^SF&E&^~D5- zK2p7Az=iq5-?R6`PvD6zD6*dbVnl6!-@bI=Xqm>VoW- zTkT)p6gy~`O^?XG@CLnpmKhudUgkb|v0S5KO;z zQG$vnPRs0L6xW=#IM3Xha(adf(<(V4mqR)3(y8zr#?hB)l~|7&yj2}uc%S*I>%NB$ za%*7rNBp@bJdt<-$^^Q7R@mDCzE>tH#pDOrZP}4<$XYx!n`cvaZ;2Y2j?3HG9@KJF zWj^a|K}ftvd(LJoAcELu9Rr!~e2G|MqhKzUb=zo|P2OiOY4KNgosXvjw&d#?@;^0w zrro{#zGA;=Jz_mMpYcvqj3F7TRLZxL@_ct)_M~!FyQ)UYVlxR?M=N7T=K%I}JDqKo z$H~;V!T6GuW(tRynRlVTSYlBG9#KNaE1eALxyPELLIj_t%~J=vlMl8EI<^*v(fV(h ze6!LFME)$I`sy-+iPmVnesi@ZDJ3!Sbo6tXbj>)MoX?c0LVAs+^fR^^mYxLXNxp?| zHK`xcP<=g*g243El;RQJXsetu{xfB}hD%jTPTSZD;*Avvl}I;_CbxSJdmq(oKH`zs zY{Gt}xN=PtDcM@Taot6Cm*^uS%qi@?TVaMleS=f{*xXgIsCT*_a2w3Y1$r)^1W>=S ze?e&Y`NQk3CYmk$^k~8DD~K;tZj8+5t#D#__ShbMeZvw;8a3MZq;K?Tm~F@kd!V-zOd=k-B1IiL$uT3ha&Eru(0jiZ#A9@nLKA$~Jycx!c-Q zKfzx-jKDTErCg%=kj4^4eyJLbGclfU09C$0e4m?|DMq^7zj3UMXYIq&aEDB-$%U`h z8wa(uJHC52PRQe$Z&2REBb@w18_Zoc`8dqA#OdKmMKQqy&)BDdj}k4TQfph1@#}M@ zcinE_4dcKBK8#xpU)z2{8kH;Ej2gNBofhjT0ELkzJEBh+B>|B z6eXEXuA{*t9TQ$~6@zf({)2)ezBdKOAF@NrPfD1%WPF*-KFc>P$;~mL(4nApry@o* z>$NU);>wO}QxcJ}w}qxt=GSNK=kUSIRjErwwq+~@AMTt4GzkRypx;Rdn?dRk2;*H9 z!N(QCt(6;@pk?~d=tH|mJ-n8b{`JznV$`PP4d>f64%-8!b95!si{9xjUN{jh8*Vm8 zg7?-i&8}=th44)vYORZOxeX+XF?wHrx{GC{*H}hvhLzxv$=%6&j8}bx7xFMLuHj0R zY4__zyG{JegVuU~C&LeUF%`3U4dZllNQt^-$}2VmU8I&*W}-*gm=)vFJL!a? zDso_T7#-3H{W!``vTlWcf2#7{xcZ?ZUEx1d_q?zp=SKdv&%pf1We{S5L+1=xX;(f9&)^f5>W~R6vY=4sDXzF9H zG4xWlG1Ep_ayKc*O7`3em?CvbF7mZu%=37s5 z+}*_(#dqF{@4u>yEB`v!=|=kJ{Fu(06Dxr|++qvHkzXHs(Y8qp`OeJz z_oKEZ5!Jf8nrQXKsQB!5y>C|fbhmt!CS>?L!Tv;E9WxGJ5M`geta2Jq-^PBRDjelJ(SZ$0T{#@)Haht&dp@L?6tj4UwNI$hp*0%fcu9hoa zeqHBRA?8E(FrCEUj%R}UY&kW@j}Z6Th~;L=uicRdj99V4Cml1ht#q$hqZg;wSg?}d zVV=44U`=ChiBpTAu9U;hqr!(bLoR1x^F$}+IzjY-lD%Mq3)mU-zn~pambZS+^LDkH zv=W&<>f6X5pF~NX^=HA1mZ~8gmQ)XLo?UZYcGjKAFuu^WuWrsEAg@JvUB7wotDu2Z zNd-AGbmI)T#Ts5m~yt7g;gFB77T+ozrCF6)!3d%VSp;7&(8nGXvq8^&|Ks;Y*Z zu$V>E#;{x^tXelXW?PaU@NIkWlk?QeNTFG(2gx)|CN@o6YI1&5#3$M=7iwMIKW(=$ z*9l%8Z75)4GbN(TzdxZB<2QSwHlX}fS#D})>$J#Rz&b#_kuXpRdt97`MD7z3?vQm&HPy(ydcsq+>aC5|okB-&g0b9CKP->x|Yk*Ue1y+m9Z zs%t0PxRNlK^)#=Wrt=$NuC+z^EoiUv z_$##v(k6KMa?#`u_X)MdB679XB24z<55;E`tJ(2~aL8##j^=ag%!+BHq&Zg`HihEr zK7L(yjOs=*@l9oR{pdC775puip#Q2+g}7?UwM!*_EMafg;^?ep3aG8FsY_Wa?l>oy zRp0Y^wpBWOq?b!oaC4F5a2==57a3zRDU#Zy;FVyE+=^Jl*Z$3-#j^HFhGl{4=vdXO zVqd?`dkYGTJ=XXjXHIvbpD%mlLa?MbIKTm;AikSoM=;aM6F#V8d@h3I5?!$zU0Z~HWG2Jz9Z_c{$^Y?SYG~4lz$WNTbLy_=9`^+VQf=1E7AHd z-`+{O6O%XC(avfA#RzfLy2a_jB+ip%IkjS&ax5#-4j>Yf|V=UJW9um6wB{&vst;sl4HpGUSh0&$HJn0XOb{f- z1ndcc;p0DIV(`uTvnHcI(LYjNUKGZE;mphM^$K=3#rKUn|) zgxq4WL+BS&9x@mO=o&K%6M)OW$*r7RpkfeU0A_VHR%FNoyft2|tmIb++1K!sSNm&@J zoGcy9Y%L$a!7@81GiNVevIoqPI( zn09wqbt^4zOh59Y^R@0%3_?um*U+@=nAbgewe@9tJ3f$>fL@R8-gA~b{6q~lgd>L# z?rjni4p~PjN6O04@2*A%Uwt&XyCl20b`QH(T~_=BH7}C-wfip1YUaJI%$(rRtjdYZ zZn{`MLY7b0n0C=7YP$Um%I3YXgXF{9P&&lxO{1Ge%?t0auX|qYvf-<%-MD(&TVdF~ zu*1|rhPjlz3ZEV!RrSj^59hA#78+SA5s^f(5qYgJKBVG|F5T7C@956VOlI74d}71= znyGzpGI2Y?fwBW~Bc|W3j=4qLO-=XG?UmDFw~EmjHqV$KWW&IdDX9qLP|u5*I>|s& zr$@lf88c-jkWm$}IX`5%z=3z}QFDqZtt%QYdR+av!VBlRgZWYMaDC`p2?O*lMh91& zvsXZjYiLzKoOom*>e%->jqQH7%I!XNjN?a7{0f8(UPn2|aPu|FDC}RE!MfSGwnpgi zRLQ)z!)3CI2Qy7X@9xL9x*Q{x#+yF$@%Pl!a<0 zPnJ1>1D@9dJRmhnD99teNlorWT^P;FzdMHMl)a*n5s+nLSm~mC&lf%MOAocMo&Z*= zQtVs|%-s0F&drjW{FGZnH#Fn?R7H39)*_{s(Gdr3J&aY$x*C-9K=Ly!zkZ0qO;{Gj ze#HCg(8r=|DGJj~E>17D!`Z4^Om6M-kSU z1n9oda{;v>X5h*Z&^h+McU=LrJAPM+0O~7$*Ps93y85okOh zJm3!K5p0kKCw z!G|6469RsP4ep)-^7#S%B9sT-DR>T2vVu<4VFTmM3b+;Mgxe&m7{g^p` zo&NQ~fOh|BPQqu{zv4p9p6>z_j(*MLe_{_<*x@Q}|Hb8m(OpzWth@Mf5?B{vTzkBT^?e7~)eDmF0tS&1x6T4m~Cr83U-7s&3+X-7= zHxBbGHj}8cb4Sazjsu(N7<4mHhw#3r3%}!!Y3hETsMPVmvzkZ$1{N0Lxkluk(RI`9 zCexFd@kOjF0_5+np-@YZ*jdpp-qXrlgXt zu9h~GlS|jX#&{!`evsmOH2#shsE(+={^k);X>eD&X1Zn$9Z^&9tI9VSa;Yk`AMeJ} z4Boxq$xHT}{yFV)Ms+$@p+coXskLC;Qe9u&RNZ3T9NnrEL8ccb6p@!kfr;s|oyV)? zS0h|9-MjjlEF$(6hpb;1CDr8LzvykAvJIArCwf3bOIS;b&&B;lM$Qed?g}c*-MMPS zs`MJ|eN6GZf{7W*N$r~^(>o66qxNi0ln=4_0`4S_JtQy;e3f~m{(h=7PSPYQe)2In zhF5q;bFzx|9f4JIF-FpBacI^P<)6M&hZA%0xFz+a`x@&83)@7mQ`Y#Q-SgIPBn#no z^Sg=t;kJn@noj6T@)pYWdnRP%Ll!7yw$irNrSICy_0FNP2(`#-i!4u*+#+f2`c7?H zsD{ul$u){A8n1t?C3y|ANrF0qFA3)BZ^d5~^oA1_g!9b;ALom_Bm;STIn^Cj511yY zP(@8?k9neb7Q);kR@_vP%K;LvV&9cqL2_5`MB9G1Ko-=E=G1aq#`S5W%p>flc3R9- zizx{g1H+7)rD)AMnof?ebloVd*kq8rJCTZ)CW?ifY24(;iS!#e*Bmcqn9`V==8{q) zOkTQdMQ0FN-ci3AR`08p(j+6C8FD|4W0;JgxRU;Xs}i-&=o4-4|?# zf4X{ec4RPoEE+3PHu~8+8M3mvp>OgJ9%Ga!9tze8NPf8WVYuNTgS-s)7zeU3D$J)V zlyQox*8vAZyqEXzd0JG4rL`L0?1j+}eX7K1Rc6?=6BnGWvr%y8KhQ6Ab-?B89 z!h{1__O_V0N3Da&B|H^wEj~)ilWU){u^fI!-&nzpIYciVfQXkycpYODvuNz*<_z1H zp#JAQH(07@M$-a5Ph)<1f-Mz+XTW5S&z*~*RZ*Oj^*voA{T{ijdJfXZFQX!(285GI zNls2pRI7sc{jeU@2OUANl9dmTr`-~!Pw0HQH65WZ0Amc zO-Al;Qo&fn>W8glzQJ{oZ{+JoYyDhIeGZWT&|N|=Utxr#)c9b27UQanfiN7+ciB~SkQjm zw7o7uZS~5=es^yQ=XoDIuBT2%>EF>jtIKK)8Op9>pC3}Rc0sC1(3{1Z0;jP!tJ2MH zev&sZyVsl6_pOwH+&YKwvu*Oub!&Xicr5p|;Z~~e!x$#sdRBW`da}w-YF?LUYpSOC znQPyZ{%$U0aclHxJ;q|Hrjw{g+niQ@$in&+Yn@;$ni3J~g8ZCMU(=j(-n_wO%x>3N zvJq(vXPp%Ol)YqkXwK4uGAdRivo1)tPc#+#>CGq6g-(g^>Oot<)Yq@`a(Q$m%1HP> zv=K~YN$70S+_F%WN(_^8Kv3<~;yAP-QM%K_5iuq+Om97nC~IeT*_z`!HwNOC6OS6gNhYp-J_)S~61 zC0eZ1e-9XxmfG5kCa9m|)7rIaHS%29rh90-^x=E6N)y8*4@H-YR2{7;J5H=B!Q+n^ z?F~)>)Ujm;7-cRO+V3^+O`DsJUd%3y{?Iw{%>y|s1CJXefFhYZ9YU z=pN%RCNi-VbH5d&L`RQfhWMQmng#P|(W;P=%_J&mEf%Vk6e|A3u1kGe(hEZ!S(|yG)+Y>bhCASOj!WGO?NTew}PpnppDDNh4 zR#V5w9MYDoptKmKPA=Ydu=JU~_4R$HSmq=a|w>_Fc=6q`eIq%lX{AaT{mz+s}eX6F;(K zIwP1Qf6Z_fs3T-FUBGjnf1f4Fm!yrJrj7f!E7^yHZ(T?eUyrFB%1)JTI=dqY5O zG`gWbN%lfZWxpuOi$XyPWktO))SFR;;dBGpS&FQLwU34Z0`>_HWlG)&9L`Jf%Wt@E z9(j_)^@}O8$vPM-*R~l-(dRhUmUj-lmdFk&iMyUo9(ItWDm#tY9^_)?zHZ9auZY=l zA{*{6d!^}ZV7>mH>!QCaOEmhW<+L;w z&sP+x6lu~(Kf9#zUNoE~UNpNSQufA;jgPY-<_~hxho^H(m$Y`G=Cg8NE7HkT^cM9@ z^AH&Bt;qB-BOWTM+uytYM;nb)81rjn^ZeE5+_ z|7(){d7k2c2GU16Aw|gijyzLxo2y2CIPcp#YhK7oFk;!*W@p78J18Lxyz3y@)tiX7 zT1L;j_4HyjI)_Ncxu&bg)9H#c0)yk@WR8Sx-z7ZSc2Z>@U%KObm38Xkt4>VTTN%fU zmy#Y&v#)J9Y}a4+!8odH+VetUoKsY{N8|;kP;X)L+Z|f@~O(~F79BKG3Pu1E-(qO~W^LTa4ee5&+e(-uFdLwqiEQ4^wr@5-7SHyuI8hWXo7>`bvO}4!9 zUbV0)5H@lvyPFy0{B{*(<-5v3Rg_297derS?*#%g*B`?+uy)X-$!O!}))S+)_K?f# zY&q{l&_80y^d@tCcE!=-I?A3w{=G!p6VXTInYCm{jd|?+RbPCKy=+sy9d%HA4y)yL z`_hTHUbZ&8G_zfCM_g6nR?@9IRRnf9$_f;7be50vb$OBFrA7nw)zlYGVPGtMD5*V*;2HF0daA1vs1xkvv3) zflO#~oE-)#1r!3ph_12*ckg=gc_oDk1GEytj8E%69q6-l!h8Gc{r*PV&zJ`P`eZ<#;gjkNZT~Y`@{hFr6i)^iu>$~- z%^WPO^c+opC-!G2`9BeRpbhZfi9LlpvkaEvxud#I7MDM~4$HGgcHw;Ftt*k^%k(7U zmN|{St#^YdJr#Y)bvL!iLi&^s!-u-rh~6l|S4{O^VQ@dEvB<TqLx(B^g(7yX>DtL6JI`7fJDVxse#Qlt&MKVB>~_H9udHcS0zY3Fyc!-*GifZa8S zK>YH=lKD#m*4~aCuf4taNa=Ze;V;iDIDPZwGO1Wz-toP8Uc+>=1T}wUCB%n^PR!SH zqP%xL3PJxf)h8K=@@wnJq8f|WM_)!V*?x}nJFkeD8a2JvM=pPh_gYkQRxb`7cIp@Y z>q|^dTC;RdW>mHrWht&l)6~cNcGGDjFL3%hHZZi>u{l|)Z@4Ln=BTFdI}7SHdk^eU zIe&?rSn>S&pf#2;L6qU+P)I<}rr%=nqNc8r^o!9QPsuKa92?YCI}_^wy8Gm@ff5Gy z78(|6Zf!CJa=@w!26jkbJ{)|5s)3dVc={Rq?A7 zq!I>y5dO>`cc<^;kMV}a9E8)p|4E7K=j{&)2Pbpjb%BPQ7H5M1!!zMes!L!3{4tr& zlA1qT_roXN*-!5uNewfwzt^)jvbHobcGNdBF(CzZ(C`GfViJ_{8`%L45B|H1$;JgS zcK-!dG%(cF)7LdLoN;h)zIb7IX7mG!2twrJ*(+uNBrKrQNcFK zFp0ix3bUZW)zplmh0SGP8&OitQSVX47^clp#+>8gLTT7>)}xxDg^HYDN*t zZq61p%BH@m#x*~W9UYT~O}#glnT9=k;}&-M+-zFLvs*?PnZ8`QZi2i>Dad}fc(_$~ zZn)3Aj}3GUvh-cQAL*6hCU=%ap&G)v=2$)DD_Rd(1?NVL`i{HDyZI|h(23`HYgug% zIga?d7Qc6qyi%IL$v-(huA1HTurI5n9jmGG;1R!n-^C{R!-tra;o*I#NS08sxm z$$st0{hIK9h$E052$T#z-j$OB-2FP=6?|%E4n+C$`L2J6BW#?f;s|v02iZ9LrJdbj zglm;Rem#Isk-w)K@K59{-2ltT-*x9dY%u~x_}|hEPS6ziqQV8#5@3L_g)h<2_8E|w z;Av-KIB@`MIVT8!!~VJcu!7CGA4&{A^iCnw$3gVz^Fh0%J?GZ_#uyP1-eBE6*{buw|R$5NR zlx;V6qlD4uLPP12YMBEbB6aU0J`;Vhh)02f-yMe%5ECqwZfCBjVUF^ixqH6?F`Xh?6s2~bI$QJsnjmRs z{Y6Js1Ok!fD^b?WwJVtu$zCR|Z-Ul0Tmn{=1(;6McgOCth2<5$?JLgkPAzSIU8QQkpta2RtyZ*H1FP?YC1{Pc#yC&pYYBE~YC zzMwFiv^v7KrS?H~_S-QhV|zTYrA0+KwBla_c?j+g`6Tb4dNL+%^Lf#XtiCf_ve%}Y z!@@n@w6&hDY^`~Sd8Ma@i?>>!Hssv7_m}}ee_eEcN{nFfJY96{IZk06uo3Xb>MUI?1~E(lt1@ILC?g<0gwk9 zC+Jvk6h;Mz>2U;K^pB54+RV^_R13~s0WJ?p1Kx~&MDTqnuwMYb1KcFYJNrQZF8M$H z7U=8W{}%i+1+Jpt^#VP=Kh+Cw+1byN6x4I34`AeeG#cJnCE&yBM-7a!Muuj3@bk!_ z4G2h^4RG~mTMYJeezh1n{0sE_HdHq=HR+E&!|zzY-H*n>Z&|_Zk1>GXvQYykPtY%t z!f)BZ?T-$@Z#k%e+T5>PPH_8U7~#1vYM>PNE0+u0!gIkYCSzu0=6I$(@bX;Lzcv5t z%l$C~@E-m4R)8GytB-&~0Cj@J9yIriJO`%KuQEU8yoeq+0^Y_1dSzg!|Ck*=1_@s0 zZxx&+>_SeCrZ)DZx2b^%ip0V2)Me~=m~cwfe(KhprA0q++2M{4~? z8Y|eT`*Ru#BzOE?4(McpasMri1-P60OBxJP`TV^cE7+d=bA4<~(3a!x<$zZ-i0At4 zH`utiAno4Y(m0ry|MDyoI~z1ne=7&J9wF0@Kc%t#r7cV>K+yS9IWTTO`2PJ_;585W zIDe}Tc$NF>_<>X0z%u+>IlyKCSoF_n5D)UFG z_#x)%*e+P$RVfM@Gdk9hk9STj%xAFB1{oM-N~qj;&-p#JyJ0v1&a0VQ=|2DFrST=J zOE#!L*AE;J7+u#j_Yvcgxi%$LZv6ub>%5lCRdS;%Xhp|J#p!Y*%Vn5gF*l{-)W z(SNLjAh(u^?2n;tOL~RUu@>Y@7-Z1nU}3!xkfEU6zc#kZ+xg>knH2E)dj3A?|GR%# l*9{-^YNhOtpYJQ4>#xZ&eQhMdy#A|OVhdv0?H-?>rXTo{DlGs2 literal 0 HcmV?d00001 diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 new file mode 100644 index 000000000..b8dc52f68 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -0,0 +1,139 @@ + +module shallow_diagnostics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: write_version_number + +use transforms_mod, only: get_grid_boundaries, & + get_deg_lon, & + get_deg_lat, & + get_grid_domain, & + get_spec_domain, & + grid_domain + +use diag_manager_mod, only: diag_axis_init, & + register_diag_field, & + register_static_field, & + send_data + +use time_manager_mod, only: time_type, & + get_time + + +use shallow_physics_mod, only: phys_type +use shallow_dynamics_mod, only: grid_type + + +implicit none +private + +public :: shallow_diagnostics_init, & + shallow_diagnostics + +character(len=84), parameter :: version = '$Id: shallow_diagnostics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=84), parameter :: tagname = '$Name: siena_201207 $' +character(len=8) :: axiset = 'shallow' +character(len=84) :: mod_name = 'shallow_diagnostics' + +logical :: module_is_initialized = .false. + +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr + +integer :: is, ie, js, je + +contains + +!----------------------------------------------------------------------------------------------------------------- +subroutine shallow_diagnostics_init(Time, lon_max, lat_max) + +type(time_type), intent(in) :: Time +integer, intent(in) :: lon_max, lat_max + +real, dimension(lon_max ) :: lon +real, dimension(lon_max+1) :: lonb +real, dimension(lat_max ) :: lat +real, dimension(lat_max+1) :: latb + +integer, dimension(2) :: axis_2d + +integer :: log_unit, id_lonb, id_lon, id_latb, id_lat +integer :: namelist_unit, ierr, io +real :: rad_to_deg +logical :: used + +call write_version_number(version, tagname) + +call get_grid_domain(is, ie, js, je) + +rad_to_deg = 45./atan(1.) +call get_grid_boundaries(lonb,latb,global=.true.) +call get_deg_lon(lon) +call get_deg_lat(lat) + +id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name=axiset, Domain2=grid_domain) +id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name=axiset, Domain2=grid_domain) +id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name=axiset, Domain2=grid_domain, edges=id_lonb) +id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name=axiset, Domain2=grid_domain, edges=id_latb) + +axis_2d(1) = id_lon +axis_2d(2) = id_lat + +id_u = register_diag_field(mod_name, 'ucomp' , axis_2d, Time, 'u_wind' , 'm/s' ) +id_v = register_diag_field(mod_name, 'vcomp' , axis_2d, Time, 'v_wind' , 'm/s' ) +id_vor = register_diag_field(mod_name, 'vor' , axis_2d, Time, 'relative vorticity' , '1/s' ) +id_div = register_diag_field(mod_name, 'div' , axis_2d, Time, 'divergence' , '1/s' ) +id_h = register_diag_field(mod_name, 'h' , axis_2d, Time, 'geopotential' , 'm2/s2' ) +id_pv = register_diag_field(mod_name, 'pv' , axis_2d, Time, 'potential vorticity' , 's/m2' ) +id_stream = register_diag_field(mod_name, 'stream', axis_2d, Time, 'streamfunction' , 'm^2/s' ) +id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) +id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) + +module_is_initialized = .true. + +return +end subroutine shallow_diagnostics_init + +!-------------------------------------------------------------------------------------------- + +subroutine shallow_diagnostics(Time, Grid, Phys, time_index) + +type(time_type), intent(in) :: Time +type(phys_type), intent(in) :: Phys +type(grid_type), intent(in) :: Grid +integer, intent(in) :: time_index + +logical :: used + +if(id_u > 0) used = send_data(id_u , Grid%u (:,:, time_index) , time) +if(id_v > 0) used = send_data(id_v , Grid%v (:,:, time_index) , time) +if(id_vor > 0) used = send_data(id_vor , Grid%vor (:,:, time_index) , time) +if(id_div > 0) used = send_data(id_div , Grid%div (:,:, time_index) , time) +if(id_h > 0) used = send_data(id_h , Grid%h (:,:, time_index) , time) +if(id_pv > 0) used = send_data(id_pv , Grid%pv (:,:) , time) +if(id_stream > 0) used = send_data(id_stream , Grid%stream (:,:) , time) +if(id_tr > 0) used = send_data(id_tr , Grid%tr (:,:, time_index) , time) +if(id_trs > 0) used = send_data(id_trs , Grid%trs (:,:, time_index) , time) + +return +end subroutine shallow_diagnostics +!-------------------------------------------------------------------------------------------- + +end module shallow_diagnostics_mod diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.html b/src/atmos_spectral_shallow/shallow_diagnostics.html new file mode 100644 index 000000000..65db99f52 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_diagnostics.html @@ -0,0 +1,150 @@ + +module shallow_diagnostics_mod + + +
+ + +

module shallow_diagnostics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The diagnostics module for the model that solves the shallow water
+   equation on the sphere  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Using the diagnostics manager, creates output files for the shallow model.
+   Variables currently available for output are
+       zonal wind 
+       meridional wind 
+       relative vorticity
+       absolute vorticity
+       streamfunction
+       spectral tracer in grid domain
+       grid tracer
+       
+   Whether or not these fields are actually output, the location of the 
+   output, the frequency of output, whether or not the output is averaged
+   in time or an instantaneous snapshot, is controlled by a 
+   diag_table file utilized by the diagnostics manager module
+       
+   One can add other diagnostics by following the (somewhat convoluted)
+       pattern within the program
+
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     diag_manaager_mod
+     transforms_mod
+     time_manager_mod
+     shallow_dynamics_mod
+     shallow_physics_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_diagnostics_mod [,only: shallow_diagnostics_init,       
+                                         shallow_diagnostics]
+                                
+
+
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_diagnostics_init
+subroutine  shallow_diagnostics
+
+
+
+
+ subroutine shallow_diagnostics_init(Time, num_lon, num_lat)
+ 
+   type(time_type)    , intent(in)     :: Time
+         current time 
+   integer, intent(in) :: num_lon, num_lat
+      num_lon = number of longitudes in global domain
+      num_lat = number of latitudes in global domain
+         
+
+   Initializes module
+
+
+
+
+ + + + subroutine shallow_diagnostics (Time, Grid, Phys, time_index) + + type(time_type), intent(in) :: Time + type(phys_type), intent(in) :: Phys + type(grid_type), intent(in) :: Grid + integer, intent(in) :: time_index + + phys_type is defined in shallow_physics_mod; ; + + grid_type is defined in shallow_dynamics_mod: + Grid contains all of the fields to be output + + many of the grid fields in grid_type are dimensioned (lon, lat, time_index) + where time_index = 1 or 2 -- the two time levels needed to update the + state of the model using a leapfrog step are toggled between (:,:,1) + and (:,:,2). The input time_index (which must equal either 1 or 2) + determines which of these two fields is output) + + (this is confusing -- the calling program needs to know what has + been placed in which slot -- it would be better to store this + information within the data type) + + + + +
+ + + diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 new file mode 100644 index 000000000..543e37230 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -0,0 +1,607 @@ +module shallow_dynamics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use time_manager_mod, only : time_type, & + get_time, & + operator(==), & + operator(-) + +use constants_mod, only : radius, omega + +use transforms_mod, only: transforms_init, transforms_end, & + get_grid_boundaries, horizontal_advection, & + trans_spherical_to_grid, trans_grid_to_spherical, & + compute_laplacian, get_eigen_laplacian, & + get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_grid_domain, get_spec_domain, & + spectral_domain, grid_domain, & + vor_div_from_uv_grid, uv_grid_from_vor_div + +use spectral_damping_mod, only: spectral_damping_init, compute_spectral_damping + +use leapfrog_mod, only: leapfrog + +use fv_advection_mod, only : fv_advection_init, a_grid_horiz_advection + +!====================================================================================== +implicit none +private +!====================================================================================== + +public :: shallow_dynamics_init, shallow_dynamics, shallow_dynamics_end, & + dynamics_type, grid_type, spectral_type, tendency_type + + +! version information +!=================================================================== +character(len=128) :: version = '$Id: shallow_dynamics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!=================================================================== + +type grid_type + real, pointer, dimension(:,:,:) :: u=>NULL(), v=>NULL(), vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL(), tr=>NULL() + real, pointer, dimension(:,:) :: stream=>NULL(), pv=>NULL() +end type +type spectral_type + complex, pointer, dimension(:,:,:) :: vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL() +end type +type tendency_type + real, pointer, dimension(:,:) :: u=>NULL(), v=>NULL(), h=>NULL(), trs=>NULL(), tr=>NULL() +end type +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat + logical :: grid_tracer, spec_tracer +end type + + + +integer, parameter :: num_time_levels = 2 + +integer :: is, ie, js, je, ms, me, ns, ne + +logical :: module_is_initialized = .false. + +real, allocatable, dimension(:) :: sin_lat, cos_lat, rad_lat, deg_lat, deg_lon, & + coriolis + +real, allocatable, dimension(:,:) :: eigen + +integer :: pe, npes + + +! namelist parameters with default values + +integer :: num_lon = 256 +integer :: num_lat = 128 +integer :: num_fourier = 85 +integer :: num_spherical = 86 +integer :: fourier_inc = 1 +! (these define a standard T85 model) + +logical :: check_fourier_imag = .false. +logical :: south_to_north = .true. +logical :: triang_trunc = .true. + +real :: robert_coeff = 0.04 +real :: robert_coeff_tracer = 0.04 +real :: longitude_origin = 0.0 + +character(len=64) :: damping_option = 'resolution_dependent' +integer :: damping_order = 4 +real :: damping_coeff = 1.e-04 +real :: h_0 = 3.e04 + +logical :: spec_tracer = .true. +logical :: grid_tracer = .true. + +real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) + +namelist /shallow_dynamics_nml/ check_fourier_imag, & + south_to_north, triang_trunc, & + num_lon, num_lat, num_fourier, & + num_spherical, fourier_inc, & + longitude_origin, damping_option, & + damping_order, damping_coeff, & + robert_coeff, robert_coeff_tracer, & + h_0, spec_tracer, grid_tracer, & + valid_range_v + +contains + +!======================================================================================= + +subroutine shallow_dynamics_init (Dyn, Time, Time_init) + +type(dynamics_type), intent(inout) :: Dyn +type(time_type) , intent(in) :: Time, Time_init + +integer :: i, j + +real, allocatable, dimension(:) :: glon_bnd, glat_bnd +real :: xx, yy, dd + +integer :: ierr, io, unit +logical :: root + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=shallow_dynamics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'shallow_dynamics_nml') + enddo + 10 call close_file (unit) +endif + +if (root) write (stdlog(), nml=shallow_dynamics_nml) + +call transforms_init(radius, num_lat, num_lon, num_fourier, fourier_inc, num_spherical, & + south_to_north=south_to_north, & + triang_trunc=triang_trunc, & + longitude_origin=longitude_origin ) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +Dyn%num_lon = num_lon +Dyn%num_lat = num_lat +Dyn%spec_tracer = spec_tracer +Dyn%grid_tracer = grid_tracer + +allocate (sin_lat (js:je)) +allocate (cos_lat (js:je)) +allocate (deg_lat (js:je)) +allocate (deg_lon (is:ie)) +allocate (coriolis (js:je)) + +call get_deg_lon (deg_lon) +call get_deg_lat (deg_lat) +call get_sin_lat (sin_lat) +call get_cos_lat (cos_lat) + +allocate (glon_bnd (num_lon + 1)) +allocate (glat_bnd (num_lat + 1)) +call get_grid_boundaries (glon_bnd, glat_bnd, global=.true.) + +coriolis = 2*omega*sin_lat + +call spectral_damping_init(damping_coeff, damping_order, damping_option, num_fourier, num_spherical, 1, 0., 0., 0.) + +allocate(eigen(ms:me,ns:ne)) +call get_eigen_laplacian(eigen) + +allocate (Dyn%spec%vor (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%spec%div (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%spec%h (ms:me, ns:ne, num_time_levels)) + +allocate (Dyn%grid%u (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%v (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%vor (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%div (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%h (is:ie, js:je, num_time_levels)) + +allocate (Dyn%tend%u (is:ie, js:je)) +allocate (Dyn%tend%v (is:ie, js:je)) +allocate (Dyn%tend%h (is:ie, js:je)) +allocate (Dyn%grid%stream (is:ie, js:je)) +allocate (Dyn%grid%pv (is:ie, js:je)) + +call fv_advection_init(num_lon, num_lat, glat_bnd, 360./float(fourier_inc)) +if(Dyn%grid_tracer) then + allocate(Dyn%Grid%tr (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%tr (is:ie, js:je)) +endif + +if(Dyn%spec_tracer) then + allocate(Dyn%Grid%trs (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%trs (is:ie, js:je)) + allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) +endif + +if(Time == Time_init) then + + Dyn%Grid%vor(:,:,1) = 0.0 + Dyn%Grid%div(:,:,1) = 0.0 + Dyn%Grid%h (:,:,1) = h_0 + + call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) + call trans_grid_to_spherical(Dyn%Grid%div(:,:,1), Dyn%Spec%div(:,:,1)) + call trans_grid_to_spherical(Dyn%Grid%h (:,:,1), Dyn%Spec%h (:,:,1)) + + call uv_grid_from_vor_div (Dyn%Spec%vor(:,:,1), Dyn%Spec%div(:,:,1), & + Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1)) + + if(Dyn%grid_tracer) then + Dyn%Grid%tr = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%tr(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%tr(:,j,1) = -1.0 + end do + endif + + if(Dyn%spec_tracer) then + Dyn%Grid%trs = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%trs(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%trs(:,j,1) = -1.0 + end do + call trans_grid_to_spherical(Dyn%Grid%trs(:,:,1), Dyn%Spec%trs(:,:,1)) + endif + +else + + call read_restart(Dyn) + +endif + +module_is_initialized = .true. + +return +end subroutine shallow_dynamics_init + +!======================================================================================== + +subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, delta_t) + +type(time_type) , intent(in) :: Time, Time_init +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in ) :: previous, current, future +real, intent(in ) :: delta_t + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +complex, dimension(ms:me, ns:ne) :: dt_vors, dt_divs, dt_hs, stream, bs, work +real, dimension(is:ie, js:je) :: vorg, bg, h_future, h_dt, dt_vorg +integer :: j + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +if(.not.module_is_initialized) then + call error_mesg('shallow_dynamics','dynamics has not been initialized ', FATAL) +endif + + +do j = js,je + vorg(:,j) = Dyn%Grid%vor(:,j,current) + coriolis(j) +end do +Dyn%Tend%u = Dyn%Tend%u + vorg*Dyn%Grid%v(:,:,current) +Dyn%Tend%v = Dyn%Tend%v - vorg*Dyn%Grid%u(:,:,current) + +call vor_div_from_uv_grid (Dyn%Tend%u, Dyn%Tend%v, dt_vors, dt_divs) + +call horizontal_advection (Dyn%Spec%h(:,:,current), & + Dyn%Grid%u(:,:,current), Dyn%Grid%v(:,:,current), Dyn%Tend%h) + +Dyn%Tend%h = Dyn%Tend%h - Dyn%Grid%h(:,:,current)*Dyn%Grid%div(:,:,current) + +call trans_grid_to_spherical (Dyn%Tend%h, dt_hs) + +bg = (Dyn%Grid%h(:,:,current) + & + 0.5*(Dyn%Grid%u(:,:,current)**2 + Dyn%Grid%v(:,:,current)**2)) + +call trans_grid_to_spherical(bg, bs) +dt_divs = dt_divs - compute_laplacian(bs) + +call implicit_correction (dt_divs, dt_hs, Dyn%Spec%div, Dyn%Spec%h, & + delta_t, previous, current) + +call compute_spectral_damping(Dyn%Spec%vor(:,:,previous), dt_vors, delta_t) +call compute_spectral_damping(Dyn%Spec%div(:,:,previous), dt_divs, delta_t) +call compute_spectral_damping(Dyn%Spec%h (:,:,previous), dt_hs , delta_t) + +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff) +call leapfrog(Dyn%Spec%div , dt_divs , previous, current, future, delta_t, robert_coeff) +call leapfrog(Dyn%Spec%h , dt_hs , previous, current, future, delta_t, robert_coeff) + +call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) +call trans_spherical_to_grid(Dyn%Spec%div(:,:,future), Dyn%Grid%div(:,:,future)) + + +call uv_grid_from_vor_div (Dyn%Spec%vor (:,:,future), Dyn%Spec%div(:,:,future), & + Dyn%Grid%u (:,:,future), Dyn%Grid%v (:,:,future)) + +call trans_spherical_to_grid (Dyn%Spec%h (:,:,future), Dyn%Grid%h(:,:,future)) + +if(minval(Dyn%Grid%v) < valid_range_v(1) .or. maxval(Dyn%Grid%v) > valid_range_v(2)) then + call error_mesg('shallow_dynamics','meridional wind out of valid range', FATAL) +endif + +if(Dyn%spec_tracer) call update_spec_tracer(Dyn%Spec%trs, Dyn%Grid%trs, Dyn%Tend%trs, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +if(Dyn%grid_tracer) call update_grid_tracer(Dyn%Grid%tr, Dyn%Tend%tr, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + + +! for diagnostics + +stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) ! for diagnostic purposes +call trans_spherical_to_grid(stream, Dyn%grid%stream) + +Dyn%Grid%pv = vorg/Dyn%Grid%h(:,:,current) + +return +end subroutine shallow_dynamics +!================================================================================ + +subroutine implicit_correction(dt_divs, dt_hs, divs, hs, delta_t, previous, current) + +complex, intent(inout), dimension(ms:,ns:) :: dt_divs, dt_hs +complex, intent(in), dimension(ms:,ns:,:) :: divs, hs +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +real :: xi, mu, mu2 + +xi = 0.5 ! centered implicit (for backwards implicit, set xi = 1.0) + +mu = xi*delta_t +mu2 = mu*mu + +dt_hs = dt_hs + h_0*(divs(:,:,current) - divs(:,:,previous)) +dt_divs = dt_divs - eigen*(hs(:,:,current) - hs(:,:,previous)) + +dt_divs = (dt_divs + mu*eigen*dt_hs)/(1.0 + mu2*eigen*h_0) +dt_hs = dt_hs - mu*h_0*dt_divs + +return +end subroutine implicit_correction + +!=================================================================================== + +subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & + previous, current, future, delta_t) + +complex, intent(inout), dimension(ms:me, ns:ne, num_time_levels) :: tr_spec +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +complex, dimension(ms:me, ns:ne) :: dt_trs + +call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) +call trans_grid_to_spherical (dt_tr, dt_trs) +call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff) +call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) + +return +end subroutine update_spec_tracer +!========================================================================== + +subroutine update_grid_tracer(tr_grid, dt_tr_grid, ug, vg, & + previous, current, future, delta_t) + +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr_grid +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg + +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +real, dimension(is:ie,js:je) :: tr_current, tr_future + +tr_future = tr_grid(:,:,previous) + delta_t*dt_tr_grid +dt_tr_grid = 0.0 +call a_grid_horiz_advection (ug(:,:,current), vg(:,:,current), tr_future, delta_t, dt_tr_grid) +tr_future = tr_future + delta_t*dt_tr_grid +tr_current = tr_grid(:,:,current) + & + robert_coeff_tracer*(tr_grid(:,:,previous) + tr_future - 2.0*tr_grid(:,:,current)) +tr_grid(:,:,current) = tr_current +tr_grid(:,:,future) = tr_future + +return +end subroutine update_grid_tracer + +!========================================================================== + +subroutine read_restart(Dyn) + +type(dynamics_type), intent(inout) :: Dyn + +integer :: unit, m, n, nt +real, dimension(ms:me, ns:ne) :: real_part, imag_part + +if(file_exist('INPUT/shallow_dynamics.res.nc')) then + do nt = 1, 2 + call read_data('INPUT/shallow_dynamics.res.nc', 'vors_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'vors_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%vor(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + call read_data('INPUT/shallow_dynamics.res.nc', 'divs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'divs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%div(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + call read_data('INPUT/shallow_dynamics.res.nc', 'hs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'hs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%h(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + if(Dyn%spec_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'trs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'trs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%trs(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + endif + call read_data('INPUT/shallow_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'div', Dyn%Grid%div(:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'h', Dyn%Grid%h (:,:,nt), grid_domain, timelevel=nt) + if(Dyn%spec_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nt), grid_domain, timelevel=nt) + endif + if(Dyn%grid_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nt), grid_domain, timelevel=nt) + endif + end do +else if(file_exist('INPUT/shallow_dynamics.res')) then + unit = open_restart_file(file='INPUT/shallow_dynamics.res',action='read') + + do nt = 1, 2 + call set_domain(spectral_domain) + call read_data(unit,Dyn%Spec%vor(:,:, nt)) + call read_data(unit,Dyn%Spec%div(:,:, nt)) + call read_data(unit,Dyn%Spec%h (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Spec%trs(:,:, nt)) + + call set_domain(grid_domain) + call read_data(unit,Dyn%Grid%u (:,:, nt)) + call read_data(unit,Dyn%Grid%v (:,:, nt)) + call read_data(unit,Dyn%Grid%vor (:,:, nt)) + call read_data(unit,Dyn%Grid%div (:,:, nt)) + call read_data(unit,Dyn%Grid%h (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Grid%trs(:,:, nt)) + if(Dyn%grid_tracer) call read_data(unit,Dyn%Grid%tr (:,:, nt)) + + end do + call close_file(unit) + +else + call error_mesg('read_restart', 'restart does not exist', FATAL) +endif + +return +end subroutine read_restart + +!==================================================================== + +subroutine write_restart(Dyn, previous, current) + +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: previous, current + +integer :: unit, nt, nn + +do nt = 1, 2 + if(nt == 1) nn = previous + if(nt == 2) nn = current + call write_data('RESTART/shallow_dynamics.res.nc', 'vors_real', real(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'vors_imag', aimag(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'divs_real', real(Dyn%Spec%div(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'divs_imag', aimag(Dyn%Spec%div(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'hs_real', real(Dyn%Spec%h (:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'hs_imag', aimag(Dyn%Spec%h (:,:,nn)), spectral_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'trs_real', real(Dyn%Spec%trs(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'trs_imag', aimag(Dyn%Spec%trs(:,:,nn)), spectral_domain) + endif + call write_data('RESTART/shallow_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'div', Dyn%Grid%div(:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'h', Dyn%Grid%h (:,:,nn), grid_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nn), grid_domain) + endif + if(Dyn%grid_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nn), grid_domain) + endif +enddo + +!unit = open_restart_file(file='RESTART/shallow_dynamics.res', action='write') + +!do n = 1, 2 +! if(n == 1) nn = previous +! if(n == 2) nn = current +! +! call set_domain(spectral_domain) +! call write_data(unit,Dyn%Spec%vor(:,:, nn)) +! call write_data(unit,Dyn%Spec%div(:,:, nn)) +! call write_data(unit,Dyn%Spec%h (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Spec%trs(:,:, nn)) +! +! call set_domain(grid_domain) +! call write_data(unit,Dyn%Grid%u (:,:, nn)) +! call write_data(unit,Dyn%Grid%v (:,:, nn)) +! call write_data(unit,Dyn%Grid%vor (:,:, nn)) +! call write_data(unit,Dyn%Grid%div (:,:, nn)) +! call write_data(unit,Dyn%Grid%h (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Grid%trs(:,:, nn)) +! if(Dyn%grid_tracer) call write_data(unit,Dyn%Grid%tr (:,:, nn)) +! +!end do + +!call close_file(unit) + +end subroutine write_restart + +!==================================================================== + +subroutine shallow_dynamics_end (Dyn, previous, current) + +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in) :: previous, current + +if(.not.module_is_initialized) then + call error_mesg('shallow_dynamics_end','dynamics has not been initialized ', FATAL) +endif + +call write_restart (Dyn, previous, current) + +call transforms_end + +module_is_initialized = .false. + +return +end subroutine shallow_dynamics_end +!=================================================================================== + +end module shallow_dynamics_mod diff --git a/src/atmos_spectral_shallow/shallow_dynamics.html b/src/atmos_spectral_shallow/shallow_dynamics.html new file mode 100644 index 000000000..600933a30 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_dynamics.html @@ -0,0 +1,370 @@ + +module shallow_dynamics_mod + + +
+ + +

module shallow_dynamics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The dynamical core of the spectral transform model for 
+   the shallow water equations on the sphere.  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Integrates the shallow water equation for hydrostatic flow of a homgeoneous,
+   incompressible fluid on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the the spectral advection
+   algorithm, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  Thinking of the model as one of
+   the upper tropopsheric flow, the default experiment involves relaxation of 
+   the geopotential to an "equilibrium value" with maxima (whose amplitude
+   and shape are controlled from the namelist) along the equator and in the 
+   subtropicals.
+
+   For a full description of the model and algorithms used, see 
+     shallow.ps 
+   
+   For higher level routines for running this shallow water model,
+   see  atmosphere_mod 
+
+
+
+ + + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     constants_mod
+     time_manager_mod
+     transforms_mod
+     spectral_damping_mod
+     leapfrog_mod
+     fv_advection_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_dynamics_mod [,only: shallow_dynamics_init,       
+                                   shallow_dynamics,
+			           shallow_dynamics_end,
+                                   dynamics_type,
+				   grid_type,
+				   spectral_type,
+				   tendency_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type grid_type + real, pointer, dimension(:,:,:) :: u, v, vor, div, h, trs, tr + real, pointer, dimension(:,:) :: stream, pv +end type + + allocated space for grid fields + + (:,:,:) => (lon, lat, time_level) + (:,:) => (lon, lat) + (lon, lat) on local computational domain + time_level stores the two time levels needed for the + leapfrog step + + u -- eastward velocity (m/s) + v -- northward velocity (m/s) + vor -- vorticity (1/s) + div -- divergence (1/s) + h -- geopotential (m^2/s^2) + trs -- tracer advected spectrally + tr -- tracer advected on grid + pv -- (f + vor)/h, where f = 2*omega*sin(lat) (s/m^2) + stream -- streamfunction (m^2/s) at current time + + + +
+ + +type spectral_type + complex, pointer, dimension(:,:,:) :: vor, div, h, trs +end type + + allocated space for spectral fields + + (:,:,:) => (zonal, meridional, time_level) + + vor -- spectral vorticity + div -- spectral divergence + h -- spectral geopotential + trs -- spectral tracer + +
+
+ +type tendency_type + real, pointer, dimension(:,:) :: u, v, h, trs, tr +end type + + allocated space for accumulating tendencies, d/dt, in grid space, + for prognostic variables + + (:,:,:) => (lon, lat) + +
+
+ +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat ! size of global domain + logical :: grid_tracer, spec_tracer +end type + + grid_tracer = .true. => tracer with gridpoint advection is beign integrated + similarly for spec_tracer + +
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_dynamics_init
+subroutine  shallow _dynamics
+subroutine  shallow_dynamics_end
+type (grid_type)
+type (spectral_type)
+type (tendency_type)
+type (dynamics_type)
+
+
+
+
+
+ subroutine shallow_dynamics_init(Dyn,  Time, Time_init)
+ 
+   type(dynamics_type), intent(inout)  :: Dyn
+         type containing all dynamical fields and related information
+	 (see type (dynamics_type))
+	 
+   type(time_type)    , intent(in)     :: Time, Time_init
+         current time and time at which integeration began
+	 time_type defined by time_manager_mod
+         
+
+   Initializes the module;
+   Reads restart from 'INPUT/shallow_dynamics.res' if Time = Time_init;
+     otherwise uses default initial conditions
+
+
+
+
+ + + + subroutine shallow_dynamics & + (Time, Time_init, Dyn, previous, current, future, delta_t) + + type(time_type) , intent(inout) :: Time, Time_init + type(dynamics_type), intent(inout) :: Dyn + integer , intent(in ) :: previous, current, future + real , intent(in ) :: delta_t + + previous, current and future = 1 or 2 + these integers refer to the third dimension of the + three-dimensional fields in Dyn + the fields at time t - delta_t are assumed to be in (:,:,previous) + the fields at time t are assumed to be in (:,:,current) + the fields at time t + delta_t are placed in (:,:,future) + overwriting whatever is already there + + delta_t = time step in seconds + + updates dynamical fields by one time step + + + +
+ + + subroutine shallow_dynamics_end(Dyn, previous, current) + + type(dynamics_type), intent(inout) :: Dyn + integer, intent(in) :: previous, current + + + Terminates module; + writes restart file to 'RESTART/shallow_dynamics.res' + + + + +
+
+ + + +
+

NAMELIST

+ +
+
+&shallow_dynamics_nml
+
+  integer :: num_lat            = 128  
+        number of latitudes in global grid
+       
+  integer :: num_lon            = 256
+        number of longitudes in global grid
+        should equal 2*num_lat for Triangular truncation
+  
+  integer :: num_fourier        = 85
+        the retained fourier wavenumber are n*fourier_inc, where
+        n ranges from 0 to num_fourier
+	 
+  integer :: num_spherical      = 86
+        the maximum number of meridional modes for any zonal wavenumber
+        for triangular truncation, set num_spherical = num_fourier +1
+         
+  integer :: fourier_inc        = 1
+        creates a "sector" model if fourier_inc > 1; integration domain is
+	(360 degrees longitude)/fourier_inc
+	
+  (the default values listed above define a standard T85 model)
+
+  logical :: check_fourier_imag = .false.
+        if true, checks to see if fields to be transformed to grid 
+	domain have zero imaginary part to their zonally symmetric
+	modes; useful for debugging
+	
+  logical :: south_to_north     = .true.
+        true => grid runs from south to north
+	false => grid runs from north to south
+	
+  logical :: triangular_trunc   = .true.
+        true  => shape of truncation is triangular
+	false => shape of truncation is rhomboidal
+
+  real    :: robert_coeff       = 0.04
+        x(current) => (1-2r)*x(current) + r*(x(future)+x(previous))
+	where r = robert_coeff (non-dimensional)
+	
+  real    :: robert_coeff_tracer       = 0.04
+        (same as robert_coeff, but for grid tracer)
+	
+  real    :: longitude_origin   = 0.0
+        longitude of first longitude, in degrees
+	(if you want the westgern boundary of first grid boc to be at 
+         0.0, set longitude_origin = 0.5*360./float(num_lon))
+	 
+  integer :: damping_option     = 'resolution_dependent'
+  integer :: damping_order      = 4
+  real    :: damping_coeff      = 1.e-04
+  
+        damping = nu*(del^2)^n where n = damping order
+	damping_option = 'resolution_dependent' or 'resolution_independent'
+	  = 'resolution_dependent' => nu is set so that the damping rate for the 
+	        mode (m=0,n=num_spherical-1) equals damping_coeff (in 1/s)
+	        For triangular truncation, damping_coeff is then the 
+	        rate of damping of the highest retained mode
+	     
+	  = 'resolution_independent' => nu = damping_coef
+	  
+  real    :: h_0  = 3.e04
+          (m^2)/(s^2) 
+          the initial condition is a state of rest with geopotential = h_0
+	  (h_0 is also used to determine the part of the divergence equation
+	   that is integrated implicitly)
+	
+  logical :: spec_tracer      = .true.
+  logical :: grid_tracer      = .true.
+       spec_tracer = true => a passive tracer is carried that is advected
+          spectrally, with the same algorithm as the vorticity
+       grid_tracer = ture => a passive tracer is carried that is advected
+          on the spectral transform grid by a finite-volume algorithm
+	  (see  shallow.ps )
+       Both tracers can be carried simultaeneously
+	  
+  real, dimension(2) :: valid_range_v = -1000., 1000.
+        A valid range for meridional wind. Model terminates if meridional wind
+	goes outside the valid range. Allows model to terminate gracefully when,
+	for example, the model becomes numerically unstable.
+
+
+ + + +
+

ERROR MESSAGES

+ +
+
+   "Dynamics has not been initialized"
+      -- shallow_dynamics_init must be called before any other
+         routines in the module are called
+	 
+   "restart does not exist" 
+      -- Time is not equal to Time_init at initalization, but the file
+          'INPUT/shallow_dynamics.res' does not exit 
+	 
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_shallow/shallow_physics.F90 b/src/atmos_spectral_shallow/shallow_physics.F90 new file mode 100644 index 000000000..8f8896d2e --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_physics.F90 @@ -0,0 +1,232 @@ +module shallow_physics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + fms_init, fms_end, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use transforms_mod, only: get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_wts_lat, & + get_grid_domain, get_spec_domain, & + grid_domain + +use time_manager_mod, only: time_type + +!======================================================================== +implicit none +private +!======================================================================== + +public :: shallow_physics_init, & + shallow_physics, & + shallow_physics_end, & + phys_type + + +! version information +!======================================================================== +character(len=128) :: version = '$Id: shallow_physics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +type phys_type + real, pointer, dimension(:,:) :: empty=>NULL() +end type + +logical :: module_is_initialized = .false. + +integer :: is, ie, js, je + +integer :: pe +logical :: root + +real, allocatable, dimension(:) :: rad_lat, deg_lat, deg_lon, & + sin_lat, cos_lat, wts_lat + +real, allocatable, dimension(:,:) :: h_eq + +real :: kappa_m, kappa_t + + + +! namelist +!======================================================================== + +real :: fric_damp_time = -20.0 +real :: therm_damp_time = -10.0 +real :: del_h = 1.e04 +real :: h_0 = 3.e04 +real :: h_amp = 2.e04 +real :: h_lon = 90.0 +real :: h_lat = 25.0 +real :: h_width = 15.0 +real :: h_itcz = 1.e05 +real :: itcz_width = 4.0 + +namelist /shallow_physics_nml/ fric_damp_time, therm_damp_time, del_h, h_0, & + h_amp, h_lon, h_lat, h_width, & + itcz_width, h_itcz +!======================================================================== + +contains + +!======================================================================== + +subroutine shallow_physics_init(Phys) + +type(phys_type), intent(inout) :: Phys + +integer :: i, j, unit, ierr, io + +real :: xx, yy, dd + +call write_version_number(version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=shallow_physics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'shallow_physics_nml') + enddo + 10 call close_file (unit) +endif + +if(fric_damp_time < 0.0) fric_damp_time = - fric_damp_time*86400 +if(therm_damp_time < 0.0) therm_damp_time = - therm_damp_time*86400 + +kappa_m = 0.0 +kappa_t = 0.0 +if( fric_damp_time .ne. 0.0) kappa_m = 1./fric_damp_time +if(therm_damp_time .ne. 0.0) kappa_t = 1./therm_damp_time + +call get_grid_domain(is,ie,js,je) + +allocate ( rad_lat (js:je) ) +allocate ( deg_lat (js:je) ) +allocate ( sin_lat (js:je) ) +allocate ( cos_lat (js:je) ) +allocate ( wts_lat (js:je) ) +allocate ( deg_lon (is:ie) ) +allocate ( h_eq (is:ie,js:je) ) + +call get_wts_lat(wts_lat) +call get_deg_lat(deg_lat) +call get_deg_lon(deg_lon) +rad_lat = deg_lat*atan(1.)/45. +sin_lat = sin(rad_lat) +cos_lat = cos(rad_lat) + + +do j = js, je + do i = is, ie + xx = (deg_lon(i) - h_lon)/(h_width*2.0) + yy = (deg_lat(j) - h_lat)/h_width + dd = xx*xx + yy*yy + h_eq(i,j) = h_0 + h_amp*max(1.e-10, exp(-dd)) + end do +end do + +do j = js, je + yy = deg_lat(j)/itcz_width + dd = yy*yy + h_eq(:,j) = h_eq(:,j) + h_itcz*exp(-dd) +end do + +!if(file_exist('INPUT/shallow_physics.res')) then +! unit = open_restart_file(file='INPUT/shallow_physics.res',action='read') +! call set_domain(grid_domain) +! call close_file(unit) +!else + +!endif + +module_is_initialized = .true. + +return +end subroutine shallow_physics_init + +!======================================================================= + +subroutine shallow_physics(Time, dt_ug, dt_vg, dt_hg, ug, vg, hg, & + delta_t, previous, current, Phys) + +real, intent(inout), dimension(is:ie, js:je) :: dt_ug, dt_vg, dt_hg +real, intent(in) , dimension(is:ie, js:je, 2) :: ug, vg, hg + +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +type(time_type), intent(in) :: Time +type(phys_type), intent(inout) :: Phys + +dt_ug = dt_ug - kappa_m*ug(:,:,previous) +dt_vg = dt_vg - kappa_m*vg(:,:,previous) +dt_hg = dt_hg - kappa_t*(hg(:,:,previous) - h_eq) + + +return +end subroutine shallow_physics + +!====================================================================== + +subroutine shallow_physics_end(Phys) + +type(phys_type), intent(in) :: Phys + +integer :: unit + +if(.not.module_is_initialized) then + call error_mesg('shallow_physics_end','physics has not been initialized ', FATAL) +endif + +!unit = open_restart_file(file='RESTART/shallow_physics.res', action='write') + +!call set_domain(grid_domain) + +!call close_file(unit) + +module_is_initialized = .false. + +return +end subroutine shallow_physics_end + +!====================================================================== + +end module shallow_physics_mod diff --git a/src/atmos_spectral_shallow/shallow_physics.html b/src/atmos_spectral_shallow/shallow_physics.html new file mode 100644 index 000000000..d3ad34980 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_physics.html @@ -0,0 +1,210 @@ + +module shallow_physics_mod + + +
+ + +

module shallow_physics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the shallow model on the sphere
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the shallow model on the sphere.  Currently adds
+   a relaxation to a specified  "equilibrium geopotential" and relaxes
+   the winds to zero
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     transforms_mod
+     time_manager_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_physics_mod [,only: shallow_physics_init,       
+                                         shallow_physics,
+					 shallow_physics_end,
+					 phys_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type phys_type + real, pointer, dimension(:,:) :: empty +end type + + fields from physics module made available for diagnostics + +
+
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_physics_init
+subroutine  shallow_physics
+subroutine  shallow_physics_end
+type(phys_type)
+
+
+
+
+
+ subroutine shallow_physics_init(Phys)
+ 
+   type(phys_type)    , intent(inout)     :: Phys
+  
+ 
+   Initializes module
+
+
+
+
+ + + + subroutine shallow_physics (Time, dt_ug, dt_vg, dt_hg, ug, vg, hg, & + delta_t, previous, current, Phys) + + real, intent(inout), dimension(:,:) :: dt_ug, dt_vg, dt_hg + + the u, v and geopotential tendencies onto which tendencies due to + the grid-point physics are added (m/(s^2) for dt_ug, dt_vg; + (m^2)/(s^3) for dt_hg) + + real, intent(in) , dimension(:,:, 2) :: ug, vg, hg + the grid zonal and meridional velocities (m/s) and + geopotential (m^2/s^2) + the third index is the time-index used in the leapfrog step + + real , intent(in) :: delta_t + time step (s) + + integer, intent(in) :: previous, current + = 1 or 2 + ug(:,:,previous) is the velocity at t-delta_t + ug(:,:,current ) is the velocity at t + + type(time_type), intent(in) :: Time + type(phys_type), intent(inout) :: Phys + + + + +
+ + + + subroutine shallow_physics_end (Phys) + + type(phys_type), intent(inout) :: Phys + + + +
+
+ + + + +
+

NAMELIST

+ +
+
+&shallow_physics_nml
+
+real    :: fric_damp_time  = -20.0
+           rate at which ua nd v are relaxed to zero (seconds)
+           (if negative, units are days instead -- negative sign is ignored) 
+      
+real    :: therm_damp_time = -10.0
+           rate at which geopotential is relaxed to h_eq
+	   (units as above)
+
+real    :: h_0             = 3.e04  (m^2/s^2)
+real    :: h_amp           = 2.e04  (m^2/s^2)
+real    :: h_lon           =  90.0  degrees
+real    :: h_lat           =  25.0  degrees
+real    :: h_width         =  15.0  degrees
+real    :: h_itcz          = 1.e05  (m^2/s^2)
+real    :: itcz_width      =  4.0   degrees
+
+           h_eq is defined as
+	   h_0 + h_amp*exp(-r^2) + h_itcz*exp(-d^2)
+	   
+	   where r^2 = xx^2 + yy^2
+	      xx = (lon - h_lon)/(2*h_width)
+	      yy = (lat - h_lat)/h_width
+	      
+	   and d = lat/itcz_width
+	  
+
+
+
+ + +
+ + + From dea4afda3d3062edb3a80332d17542543c18ce4c Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 14 May 2019 16:29:41 +0100 Subject: [PATCH 02/34] Copied example input and diag file entries into test case, and set up shallow code base again. --- .../shallow_water/shallow_water_test.py | 107 ++++++++++++++++++ src/extra/python/isca/__init__.py | 2 +- src/extra/python/isca/codebase.py | 10 +- 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 exp/test_cases/shallow_water/shallow_water_test.py diff --git a/exp/test_cases/shallow_water/shallow_water_test.py b/exp/test_cases/shallow_water/shallow_water_test.py new file mode 100644 index 000000000..b2d20f8a4 --- /dev/null +++ b/exp/test_cases/shallow_water/shallow_water_test.py @@ -0,0 +1,107 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) + + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml' + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : .true., + 'spec_tracer' : .true., + 'robert_coeff' : 0.04 + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : -50.0, + 'therm_damp_time' : -10.0, + 'del_h' : 2.e04, + 'h_0' : 3.e04, + 'h_amp' : 1.e05, + 'h_lon' : 90.0, + 'h_lat' : 25.0, + 'h_width' : 15.0, + 'itcz_width' : 4.0, + 'h_itcz' : 4.e04, + }, +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/src/extra/python/isca/__init__.py b/src/extra/python/isca/__init__.py index 495c24e12..1ab4fe8e5 100644 --- a/src/extra/python/isca/__init__.py +++ b/src/extra/python/isca/__init__.py @@ -77,4 +77,4 @@ def emit(self, event, *args, **kwargs): from isca.experiment import Experiment, DiagTable, Namelist, FailedRunError -from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase #, ShallowCodeBase +from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase diff --git a/src/extra/python/isca/codebase.py b/src/extra/python/isca/codebase.py index b9863999f..8cd57b27a 100644 --- a/src/extra/python/isca/codebase.py +++ b/src/extra/python/isca/codebase.py @@ -303,8 +303,8 @@ class DryCodeBase(GreyCodeBase): -# class ShallowCodeBase(CodeBase): -# """The Shallow Water Equations. -# """ -# name = 'shallow' -# executable_name = 'shallow.x' +class ShallowCodeBase(CodeBase): + """The Shallow Water Equations. + """ + name = 'shallow' + executable_name = 'shallow.x' From a5a6c8e0cc3916110771a8147b7f5138eee0b597 Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 14 May 2019 16:47:42 +0100 Subject: [PATCH 03/34] updated experiment to get it to run, and updated leapfrog and damping options in shallow water to get in line with Isca modifications. --- .../shallow_water/shallow_water_test.py | 9 +++++---- src/atmos_spectral_shallow/shallow_dynamics.F90 | 17 ++++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/exp/test_cases/shallow_water/shallow_water_test.py b/exp/test_cases/shallow_water/shallow_water_test.py index b2d20f8a4..53359d36c 100644 --- a/exp/test_cases/shallow_water/shallow_water_test.py +++ b/exp/test_cases/shallow_water/shallow_water_test.py @@ -54,6 +54,7 @@ 'minutes': 0, 'seconds': 0, 'dt_atmos': 1200, + 'calendar': 'no_calendar', }, 'atmosphere_nml':{ @@ -65,7 +66,7 @@ 'fileset_write': 'single' }, - 'fms_nml' + 'fms_nml':{ 'print_memory_usage':True, 'domains_stack_size': 200000, }, @@ -80,9 +81,9 @@ 'damping_order' : 4, 'damping_coeff' : 1.e-04, 'h_0' : 3.e04, - 'grid_tracer' : .true., - 'spec_tracer' : .true., - 'robert_coeff' : 0.04 + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, 'robert_coeff_tracer' : 0.04, }, diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 543e37230..98f674084 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -112,7 +112,8 @@ module shallow_dynamics_mod integer :: num_lat = 128 integer :: num_fourier = 85 integer :: num_spherical = 86 -integer :: fourier_inc = 1 +integer :: fourier_inc = 1 +integer :: cutoff_wn = 30 ! (these define a standard T85 model) logical :: check_fourier_imag = .false. @@ -122,6 +123,7 @@ module shallow_dynamics_mod real :: robert_coeff = 0.04 real :: robert_coeff_tracer = 0.04 real :: longitude_origin = 0.0 +real :: raw_filter_coeff = 1.0 character(len=64) :: damping_option = 'resolution_dependent' integer :: damping_order = 4 @@ -141,7 +143,8 @@ module shallow_dynamics_mod damping_order, damping_coeff, & robert_coeff, robert_coeff_tracer, & h_0, spec_tracer, grid_tracer, & - valid_range_v + valid_range_v, cutoff_wn, & + raw_filter_coeff contains @@ -209,7 +212,7 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init) coriolis = 2*omega*sin_lat -call spectral_damping_init(damping_coeff, damping_order, damping_option, num_fourier, num_spherical, 1, 0., 0., 0.) +call spectral_damping_init(damping_coeff, damping_order, damping_option, cutoff_wn, num_fourier, num_spherical, 1, 0., 0., 0.) allocate(eigen(ms:me,ns:ne)) call get_eigen_laplacian(eigen) @@ -333,9 +336,9 @@ subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, del call compute_spectral_damping(Dyn%Spec%div(:,:,previous), dt_divs, delta_t) call compute_spectral_damping(Dyn%Spec%h (:,:,previous), dt_hs , delta_t) -call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff) -call leapfrog(Dyn%Spec%div , dt_divs , previous, current, future, delta_t, robert_coeff) -call leapfrog(Dyn%Spec%h , dt_hs , previous, current, future, delta_t, robert_coeff) +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call leapfrog(Dyn%Spec%div , dt_divs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call leapfrog(Dyn%Spec%h , dt_hs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) call trans_spherical_to_grid(Dyn%Spec%div(:,:,future), Dyn%Grid%div(:,:,future)) @@ -408,7 +411,7 @@ subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) call trans_grid_to_spherical (dt_tr, dt_trs) call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) -call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) return From bd7df0686d49b62af7d24bbc395c5ae3ac0ad5a3 Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 14 May 2019 17:15:33 +0100 Subject: [PATCH 04/34] Integrated barotropic vorticity equation into Isca framework too. Needed same fixes as shallow dynamics. --- .../barotropic_vor_eq_stirring_test.py | 116 ++++++++++++++++++ .../barotropic_vor_eq_test.py | 100 +++++++++++++++ .../barotropic_dynamics.F90 | 11 +- src/extra/python/isca/__init__.py | 2 +- src/extra/python/isca/codebase.py | 6 + 5 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py create mode 100644 exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py new file mode 100644 index 000000000..e03867687 --- /dev/null +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py @@ -0,0 +1,116 @@ +import os + +import numpy as np + +from isca import BarotropicCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = BarotropicCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('barotropic_stirring_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('barotropic_diagnostics', 'ucomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vcomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'pv', time_avg=True) +diag.add_field('barotropic_diagnostics', 'stream', time_avg=True) +diag.add_field('barotropic_diagnostics', 'trs', time_avg=True) +diag.add_field('barotropic_diagnostics', 'tr', time_avg=True) +diag.add_field('barotropic_diagnostics', 'eddy_vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'delta_u', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'barotropic_dynamics_nml':{ + 'triang_trunc' : True, + 'num_lat' : 128, + 'num_lon' : 256, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 2, + 'damping_coeff' : 1.157E-4, + 'damping_coeff_r': 1.929E-6, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'm_0' : 6, + 'zeta_0' : 0.0, + 'eddy_lat' : 45.0, + 'eddy_width' : 10.0, + 'robert_coeff' : 0.04, + 'initial_zonal_wind' : 'zero', + }, + + 'barotropic_physics_nml':{ + }, + + 'stirring_nml': { + 'decay_time':172800, + 'amplitude':3.e-11, + 'lat0':45., + 'lon0':180., + 'widthy':12., + 'widthx':45., + 'B':1.0, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py new file mode 100644 index 000000000..fec772b66 --- /dev/null +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py @@ -0,0 +1,100 @@ +import os + +import numpy as np + +from isca import BarotropicCodebase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = BarotropicCodebase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('barotropic_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('barotropic_diagnostics', 'ucomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vcomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'pv', time_avg=True) +diag.add_field('barotropic_diagnostics', 'stream', time_avg=True) +diag.add_field('barotropic_diagnostics', 'trs', time_avg=True) +diag.add_field('barotropic_diagnostics', 'tr', time_avg=True) +diag.add_field('barotropic_diagnostics', 'eddy_vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'delta_u', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'barotropic_dynamics_nml':{ + 'triang_trunc' : True, + 'num_lat' : 128, + 'num_lon' : 256, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'm_0' : 4, + 'zeta_0' : 8.e-05, + 'eddy_lat' : 45.0, + 'eddy_width' : 15.0, + 'robert_coeff' : 0.04, + }, + + 'barotropic_physics_nml':{ + }, +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 index 07922fdbc..64be9cd8d 100644 --- a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 @@ -122,6 +122,7 @@ module barotropic_dynamics_mod real :: robert_coeff = 0.04 real :: longitude_origin = 0.0 +real :: raw_filter_coeff = 1.0 character(len=64) :: damping_option = 'resolution_dependent' integer :: damping_order = 4 @@ -141,6 +142,7 @@ module barotropic_dynamics_mod integer :: num_fourier = 85 integer :: num_spherical = 86 integer :: fourier_inc = 1 +integer :: cutoff_wn = 30 real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) character(len=64) :: initial_zonal_wind = 'two_jets' @@ -154,7 +156,8 @@ module barotropic_dynamics_mod damping_coeff_r, robert_coeff, & spec_tracer, grid_tracer, & eddy_lat, eddy_width, zeta_0, m_0, & - valid_range_v, initial_zonal_wind + valid_range_v, initial_zonal_wind, & + cutoff_wn contains @@ -231,7 +234,7 @@ subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_l rad_lat = deg_lat*atan(1.0)/45.0 rad_lon = deg_lon*atan(1.0)/45.0 -call spectral_damping_init(damping_coeff, damping_order, damping_option, num_fourier, num_spherical, 1, 0., 0., 0., & +call spectral_damping_init(damping_coeff, damping_order, damping_option, cutoff_wn, num_fourier, num_spherical, 1, 0., 0., 0., & damping_coeff_r=damping_coeff_r) call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) @@ -363,7 +366,7 @@ subroutine barotropic_dynamics(Time, Time_init, Dyn, previous, current, future, call stirring(Time, dt_vors) -call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff) +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) @@ -404,7 +407,7 @@ subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) call trans_grid_to_spherical (dt_tr, dt_trs) call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) -call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) return diff --git a/src/extra/python/isca/__init__.py b/src/extra/python/isca/__init__.py index 1ab4fe8e5..6573fdf85 100644 --- a/src/extra/python/isca/__init__.py +++ b/src/extra/python/isca/__init__.py @@ -77,4 +77,4 @@ def emit(self, event, *args, **kwargs): from isca.experiment import Experiment, DiagTable, Namelist, FailedRunError -from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase +from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase, BarotropicCodebase diff --git a/src/extra/python/isca/codebase.py b/src/extra/python/isca/codebase.py index 8cd57b27a..d29f661dc 100644 --- a/src/extra/python/isca/codebase.py +++ b/src/extra/python/isca/codebase.py @@ -308,3 +308,9 @@ class ShallowCodeBase(CodeBase): """ name = 'shallow' executable_name = 'shallow.x' + +class BarotropicCodebase(CodeBase): + """The Barotropic vorticity equations. + """ + name = 'barotropic' + executable_name = 'barotropic_isca.x' \ No newline at end of file From 4c22f20c342c753c6470514566d21fc20d5827dd Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 14 May 2019 17:18:04 +0100 Subject: [PATCH 05/34] Fixed small typo in barotropic codebase object. --- .../barotropic_vorticity_equation/barotropic_vor_eq_test.py | 4 ++-- src/extra/python/isca/__init__.py | 2 +- src/extra/python/isca/codebase.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py index fec772b66..01cdcc687 100644 --- a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py @@ -2,13 +2,13 @@ import numpy as np -from isca import BarotropicCodebase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca import BarotropicCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE NCORES = 8 base_dir = os.path.dirname(os.path.realpath(__file__)) # a CodeBase can be a directory on the computer, # useful for iterative development -cb = BarotropicCodebase.from_directory(GFDL_BASE) +cb = BarotropicCodeBase.from_directory(GFDL_BASE) # or it can point to a specific git repo and commit id. # This method should ensure future, independent, reproducibility of results. diff --git a/src/extra/python/isca/__init__.py b/src/extra/python/isca/__init__.py index 6573fdf85..cd9e8af1b 100644 --- a/src/extra/python/isca/__init__.py +++ b/src/extra/python/isca/__init__.py @@ -77,4 +77,4 @@ def emit(self, event, *args, **kwargs): from isca.experiment import Experiment, DiagTable, Namelist, FailedRunError -from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase, BarotropicCodebase +from isca.codebase import IscaCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase, BarotropicCodeBase diff --git a/src/extra/python/isca/codebase.py b/src/extra/python/isca/codebase.py index d29f661dc..8ce9b35b7 100644 --- a/src/extra/python/isca/codebase.py +++ b/src/extra/python/isca/codebase.py @@ -309,7 +309,7 @@ class ShallowCodeBase(CodeBase): name = 'shallow' executable_name = 'shallow.x' -class BarotropicCodebase(CodeBase): +class BarotropicCodeBase(CodeBase): """The Barotropic vorticity equations. """ name = 'barotropic' From 717a94abe0bed959dc7b9e8e5929ac645edea675 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 10:11:41 +0100 Subject: [PATCH 06/34] Rewrite of stirring module to retain functionality, but to get rid of it defining grid for output. This should make it possible to use stirring in the shallow water model. --- src/atmos_spectral_barotropic/atmosphere.F90 | 6 +++- .../barotropic_diagnostics.F90 | 28 +++++++++++++++++-- .../barotropic_dynamics.F90 | 6 ++-- src/atmos_spectral_barotropic/stirring.F90 | 9 +----- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/atmos_spectral_barotropic/atmosphere.F90 b/src/atmos_spectral_barotropic/atmosphere.F90 index 40a01ebb1..6b878fe98 100644 --- a/src/atmos_spectral_barotropic/atmosphere.F90 +++ b/src/atmos_spectral_barotropic/atmosphere.F90 @@ -63,6 +63,9 @@ module atmosphere_mod use barotropic_diagnostics_mod, only: barotropic_diagnostics_init, & barotropic_diagnostics +use stirring_mod, only: stirring_init + + !======================================================================== implicit none private @@ -142,7 +145,7 @@ subroutine atmosphere_init(Time_init_in, Time, Time_step_in) call write_version_number(version, tagname) if (root) write (stdlog(), nml=atmosphere_nml) -call barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_lat, id_lonb, id_latb) +call barotropic_dynamics_init (Dyn, Time, Time_init, dt_real) call get_grid_domain(is,ie,js,je) call get_spec_domain(ms,me,ns,ne) @@ -155,6 +158,7 @@ subroutine atmosphere_init(Time_init_in, Time, Time_step_in) call barotropic_physics_init(Phys) call barotropic_diagnostics_init(Time, num_lon, num_lat, id_lon, id_lat, id_lonb, id_latb) +call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) if(Time == Time_init) then previous = 1 diff --git a/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 index 69cfc096e..fa632893a 100644 --- a/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 +++ b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 @@ -28,11 +28,15 @@ module barotropic_diagnostics_mod use transforms_mod, only: get_grid_domain, & get_spec_domain, & grid_domain, & - trans_spherical_to_grid + trans_spherical_to_grid, & + get_deg_lon, & + get_deg_lat, & + get_grid_boundaries use diag_manager_mod, only: register_diag_field, & register_static_field, & - send_data + send_data, & + diag_axis_init use time_manager_mod, only: time_type, & get_time @@ -63,10 +67,18 @@ module barotropic_diagnostics_mod subroutine barotropic_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb) type(time_type), intent(in) :: Time -integer, intent(in) :: lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb +integer, intent(in) :: lon_max, lat_max +integer, intent(out):: id_lon, id_lat, id_lonb, id_latb + +real, dimension(lon_max ) :: lon +real, dimension(lon_max+1) :: lonb +real, dimension(lat_max ) :: lat +real, dimension(lat_max+1) :: latb integer, dimension(2) :: axis_2d +real :: rad_to_deg + integer :: log_unit integer :: namelist_unit, ierr, io logical :: used @@ -76,6 +88,16 @@ subroutine barotropic_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, i call get_grid_domain(is, ie, js, je) call get_spec_domain(ms, me, ns, ne) +call get_deg_lon(lon) +call get_deg_lat(lat) +call get_grid_boundaries(lonb,latb,global=.true.) + +rad_to_deg = 45./atan(1.) +id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name=axiset, Domain2=grid_domain) +id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name=axiset, Domain2=grid_domain) +id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name=axiset, Domain2=grid_domain, edges=id_lonb) +id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name=axiset, Domain2=grid_domain, edges=id_latb) + axis_2d(1) = id_lon axis_2d(2) = id_lat diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 index 64be9cd8d..ac18b6127 100644 --- a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 @@ -61,7 +61,7 @@ module barotropic_dynamics_mod use fv_advection_mod, only: fv_advection_init, & a_grid_horiz_advection -use stirring_mod, only: stirring_init, stirring, stirring_end +use stirring_mod, only: stirring, stirring_end !=============================================================================================== implicit none @@ -163,12 +163,11 @@ module barotropic_dynamics_mod !=============================================================================================== -subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_lat, id_lonb, id_latb) +subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real) type(dynamics_type), intent(inout) :: Dyn type(time_type) , intent(in) :: Time, Time_init real, intent(in) :: dt_real -integer, intent(out) :: id_lon, id_lat, id_lonb, id_latb integer :: i, j @@ -236,7 +235,6 @@ subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real, id_lon, id_l call spectral_damping_init(damping_coeff, damping_order, damping_option, cutoff_wn, num_fourier, num_spherical, 1, 0., 0., 0., & damping_coeff_r=damping_coeff_r) -call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) allocate (Dyn%spec%vor (ms:me, ns:ne, num_time_levels)) allocate (Dyn%grid%u (is:ie, js:je, num_time_levels)) diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 index 04bdfc280..88aad46a8 100644 --- a/src/atmos_spectral_barotropic/stirring.F90 +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -77,7 +77,7 @@ module stirring_mod subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) real, intent(in) :: dt type(time_type), intent(in) :: Time -integer, intent(out) :: id_lon, id_lat, id_lonb, id_latb +integer, intent(in) :: id_lon, id_lat, id_lonb, id_latb real :: xx, kk, rad_to_deg integer :: i,j,m,n,ierr,io,unit,lon_max,lat_max real, allocatable, dimension(:) :: ampx, ampy, lon, lat, lonb, latb @@ -108,13 +108,6 @@ subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) call get_deg_lon(lon) call get_deg_lat(lat) -call get_grid_boundaries(lonb,latb,global=.true.) - -rad_to_deg = 180./pi -id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name='barotropic', Domain2=grid_domain) -id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name='barotropic', Domain2=grid_domain) -id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name='barotropic', Domain2=grid_domain, edges=id_lonb) -id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name='barotropic', Domain2=grid_domain, edges=id_latb) module_is_initialized = .true. if(amplitude == 0.0) return ! stirring does nothing more unless amplitude is non-zero From 2f6acc087a84ebd6531cd1d5692e79645928136a Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 10:20:05 +0100 Subject: [PATCH 07/34] Updated shallow code to include stirring, but thanks to rewrite of stirring, we can output stirring variables from within shallow water framework. --- src/atmos_spectral_shallow/atmosphere.F90 | 8 +++++--- src/atmos_spectral_shallow/shallow_diagnostics.F90 | 5 +++-- src/atmos_spectral_shallow/shallow_dynamics.F90 | 10 ++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/atmos_spectral_shallow/atmosphere.F90 b/src/atmos_spectral_shallow/atmosphere.F90 index 3b59b37fb..7336f9b72 100644 --- a/src/atmos_spectral_shallow/atmosphere.F90 +++ b/src/atmos_spectral_shallow/atmosphere.F90 @@ -67,6 +67,7 @@ Module atmosphere_mod use shallow_diagnostics_mod, only : shallow_diagnostics_init, & shallow_diagnostics +use stirring_mod, only: stirring_init !======================================================================== implicit none @@ -124,7 +125,7 @@ subroutine atmosphere_init(Time_init_in, Time, Time_step_in) type (time_type), intent(in) :: Time_init_in, Time, Time_step_in -integer :: i, j, n, nn, ierr, io, unit +integer :: i, j, n, nn, ierr, io, unit, id_lon, id_lat, id_lonb, id_latb integer :: nlon, nlat pe = mpp_pe() @@ -151,7 +152,7 @@ subroutine atmosphere_init(Time_init_in, Time, Time_step_in) call write_version_number(version, tagname) if (root) write (stdlog(), nml=atmosphere_nml) -call shallow_dynamics_init (Dyn, Time, Time_init) +call shallow_dynamics_init (Dyn, Time, Time_init, dt_real) call get_grid_domain(is,ie,js,je) call get_spec_domain(ms,me,ns,ne) @@ -163,7 +164,8 @@ subroutine atmosphere_init(Time_init_in, Time, Time_step_in) nlat = je+1-js call shallow_physics_init(Phys) -call shallow_diagnostics_init(Time, num_lon, num_lat) +call shallow_diagnostics_init(Time, num_lon, num_lat, id_lon, id_lat, id_lonb, id_latb) +call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) if(Time == Time_init) then previous = 1 diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 index b8dc52f68..e0fd0052a 100644 --- a/src/atmos_spectral_shallow/shallow_diagnostics.F90 +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -62,10 +62,11 @@ module shallow_diagnostics_mod contains !----------------------------------------------------------------------------------------------------------------- -subroutine shallow_diagnostics_init(Time, lon_max, lat_max) +subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb) type(time_type), intent(in) :: Time integer, intent(in) :: lon_max, lat_max +integer, intent(out):: id_lon, id_lat, id_lonb, id_latb real, dimension(lon_max ) :: lon real, dimension(lon_max+1) :: lonb @@ -74,7 +75,7 @@ subroutine shallow_diagnostics_init(Time, lon_max, lat_max) integer, dimension(2) :: axis_2d -integer :: log_unit, id_lonb, id_lon, id_latb, id_lat +integer :: log_unit integer :: namelist_unit, ierr, io real :: rad_to_deg logical :: used diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 98f674084..eacbfc159 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -57,6 +57,8 @@ module shallow_dynamics_mod use fv_advection_mod, only : fv_advection_init, a_grid_horiz_advection +use stirring_mod, only : stirring, stirring_end + !====================================================================================== implicit none private @@ -150,17 +152,18 @@ module shallow_dynamics_mod !======================================================================================= -subroutine shallow_dynamics_init (Dyn, Time, Time_init) +subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) type(dynamics_type), intent(inout) :: Dyn type(time_type) , intent(in) :: Time, Time_init +real , intent(in) :: dt_real integer :: i, j real, allocatable, dimension(:) :: glon_bnd, glat_bnd real :: xx, yy, dd -integer :: ierr, io, unit +integer :: ierr, io, unit, id_lon, id_lat, id_lonb, id_latb logical :: root ! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > @@ -336,6 +339,8 @@ subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, del call compute_spectral_damping(Dyn%Spec%div(:,:,previous), dt_divs, delta_t) call compute_spectral_damping(Dyn%Spec%h (:,:,previous), dt_hs , delta_t) +call stirring(Time, dt_vors) + call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call leapfrog(Dyn%Spec%div , dt_divs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) call leapfrog(Dyn%Spec%h , dt_hs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) @@ -600,6 +605,7 @@ subroutine shallow_dynamics_end (Dyn, previous, current) call write_restart (Dyn, previous, current) call transforms_end +call stirring_end module_is_initialized = .false. From 6c696ee3c208bc1a4db138bd3c7dba4457a8348b Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 10:23:39 +0100 Subject: [PATCH 08/34] Remove needless string definitions. --- src/atmos_spectral_barotropic/stirring.F90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 index 88aad46a8..7ceafdb82 100644 --- a/src/atmos_spectral_barotropic/stirring.F90 +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -53,8 +53,6 @@ module stirring_mod integer, allocatable, dimension(:) :: seed ! random number seed real :: astir, bstir integer :: num_steps, num_fourier, num_spherical, nseed -character(len=8) :: axiset = 'barotropic' -character(len=84) :: mod_name = 'barotropic_diagnostics' logical :: module_is_initialized = .false. From 993966c732a603b51a07fd99b5a276b1e8a3707e Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 11:16:02 +0100 Subject: [PATCH 09/34] Adding comments to stirring file to point out origin. --- .../shallow_water_stirring_test.py | 126 ++++++++++++++++++ src/atmos_spectral_barotropic/stirring.F90 | 2 +- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 exp/test_cases/shallow_water/shallow_water_stirring_test.py diff --git a/exp/test_cases/shallow_water/shallow_water_stirring_test.py b/exp/test_cases/shallow_water/shallow_water_stirring_test.py new file mode 100644 index 000000000..d6571c2c1 --- /dev/null +++ b/exp/test_cases/shallow_water/shallow_water_stirring_test.py @@ -0,0 +1,126 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_stirring_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : -50.0, + 'therm_damp_time' : -10.0, + 'del_h' : 2.e04, + 'h_0' : 3.e04, + 'h_amp' : 1.e05, + 'h_lon' : 90.0, + 'h_lat' : 25.0, + 'h_width' : 15.0, + 'itcz_width' : 4.0, + 'h_itcz' : 4.e04, + }, + +#The below stirring parameters are those of Vallis et al 2004 DOI: 10.1175/1520-0469(2004)061<0264:AMASDM>2.0.CO;2 +#They have a decorrelation time set by 'decay_time', chosen to be 2 days by default. The forcing is also localised in +#latitude and longitude, with the centre of the forcing set by lat0 and lon0, and the width of the gaussians set by +#'widthy and widthx'. B sets the variation in longitude, with ampx = 1 + B*exp(-xx/widthx**2) + + 'stirring_nml': { + 'decay_time':172800, + 'amplitude':3.e-13, + 'lat0':45., + 'lon0':180., + 'widthy':12., + 'widthx':45., + 'B':1.0, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,122): + exp.run(i, num_cores=NCORES) diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 index 7ceafdb82..687fb81f9 100644 --- a/src/atmos_spectral_barotropic/stirring.F90 +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -210,7 +210,7 @@ subroutine stirring(Time, dt_vors) if(ms == 0 .and. ns == 0) then new_stirring(0,0)=cmplx(0.0,0.0) ! A non-zero global mean is introduced by the grid space computation, but we don't want it. endif -s_stir = bstir*s_stir + new_stirring +s_stir = bstir*s_stir + new_stirring !This is equation A.6 in Vallis et al 2004 - DOI:10.1175/1520-0469(2004)061<0264:AMASDM>2.0.CO;2 dt_vors = dt_vors + s_stir call trans_spherical_to_grid(s_stir,g_stir) From 4693d72a31bf6286422c370f9a4d2912b509b0a1 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 14:12:44 +0100 Subject: [PATCH 10/34] Making the wavenumber range of the forcing a namelist parameter set. --- src/atmos_spectral_barotropic/stirring.F90 | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 index 687fb81f9..7de10c3f1 100644 --- a/src/atmos_spectral_barotropic/stirring.F90 +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -62,12 +62,16 @@ module stirring_mod public :: stirring_init, stirring, stirring_end real :: decay_time=2*86400, amplitude=0.0, lat0=45., widthy=12. +logical :: do_localize=.true.!Default true to allow forcing to be localized in physical space. Set to false to have forcing everywhere. ! Set B to a non-zero value for stirring that has zonal structure. ! The strength of the stirring at latitude=lat0 is: amplitude*(1.0 + B*exp(-.5*((lon-lon0)/widthx)**2)) -real :: lon0=180., B=0.0, widthx=45. ! widthx +real :: lon0=180., B=0.0, widthx=45., C=1.0 ! widthx +integer :: n_total_forcing_max = 15 !total wavenumbers LESS THAN this number will be forced +integer :: n_total_forcing_min = 9 !total wavenumbers GREATER THAN this number will be forced +integer :: zonal_forcing_min = 3 !Zonal wavenumbers GREATER THAN this number will be forced, subject to total wavenumber constraints -namelist / stirring_nml / decay_time, amplitude, lat0, lon0, widthy, widthx, B +namelist / stirring_nml / decay_time, amplitude, lat0, lon0, widthy, widthx, B, do_localize, n_total_forcing_max, n_total_forcing_min, zonal_forcing_min contains @@ -123,9 +127,9 @@ subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) allocate(g_stir_sqr(is:ie,js:je)); g_stir_sqr = 0.0 ! wave_mask is .true. when (m+n > 9) .and. (m+n < 15) .and. (m > 3) -do m=4,14 +do m=(zonal_forcing_min+1),(n_total_forcing_max-1) if(m >= ms .and. m <= me) then - do n=10-m,14-m + do n=(n_total_forcing_min+1)-m,(n_total_forcing_max-1)-m if(n >= ns .and. n <= ne) then wave_mask(m,n) = .true. endif @@ -146,11 +150,16 @@ subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) do j=js,je ampy(j) = exp(-.5*((lat(j)-lat0)/widthy)**2) enddo -do j=js,je -do i=is,ie - localize(i,j) = ampx(i)*ampy(j) -enddo -enddo +if (do_localize) then + do j=js,je + do i=is,ie + localize(i,j) = ampx(i)*ampy(j) + enddo + enddo +else + localize = 1.0 +endif + deallocate(ampx, ampy) num_steps = 0 From f8fcd5c1fa9f34c93d4bcffac15ef70ced110c59 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 14:13:08 +0100 Subject: [PATCH 11/34] Trying to make scott and polvani 2007 esque flows. --- ...shallow_water_small_scale_stirring_test.py | 151 ++++++++++++++++++ ...iant_planet_shallow_water_stirring_test.py | 148 +++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py create mode 100644 exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py diff --git a/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py b/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py new file mode 100644 index 000000000..a45007b8b --- /dev/null +++ b/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py @@ -0,0 +1,151 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for number_ld_in_radius_units in [10.]: + + exp = Experiment('giant_shallow_stirring_test_experiment_uniform_small_scale_stirring_mk3', codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * 1., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13, + 'n_total_forcing_max': 45, + 'n_total_forcing_min': 39, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,122): + exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py b/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py new file mode 100644 index 000000000..fa19bc3c5 --- /dev/null +++ b/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py @@ -0,0 +1,148 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for number_ld_in_radius_units in [10.]: + + exp = Experiment('giant_shallow_stirring_test_experiment_uniform_stirring_mk3', codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * 1., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13, + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,122): + exp.run(i, num_cores=NCORES) From a2796269c0530356a675bee8cecdaf12ee7aad5f Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 16 May 2019 14:15:51 +0100 Subject: [PATCH 12/34] Adding missing model files that I forgot to commit. These are necessary for compilation of new codes. --- src/extra/model/barotropic/field_table | 0 src/extra/model/barotropic/path_names | 138 +++++++++++++++++++++++++ src/extra/model/shallow/path_names | 138 +++++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 src/extra/model/barotropic/field_table create mode 100644 src/extra/model/barotropic/path_names create mode 100644 src/extra/model/shallow/path_names diff --git a/src/extra/model/barotropic/field_table b/src/extra/model/barotropic/field_table new file mode 100644 index 000000000..e69de29bb diff --git a/src/extra/model/barotropic/path_names b/src/extra/model/barotropic/path_names new file mode 100644 index 000000000..e7c619d86 --- /dev/null +++ b/src/extra/model/barotropic/path_names @@ -0,0 +1,138 @@ +atmos_solo/atmos_model.F90 +atmos_spectral_barotropic/atmosphere.F90 +atmos_spectral_barotropic/barotropic_diagnostics.F90 +atmos_spectral_barotropic/barotropic_dynamics.F90 +atmos_spectral_barotropic/barotropic_physics.F90 +atmos_spectral_barotropic/stirring.F90 +atmos_spectral/model/fv_advection.F90 +atmos_spectral/model/leapfrog.F90 +atmos_spectral/model/spectral_damping.F90 +atmos_spectral/tools/gauss_and_legendre.F90 +atmos_spectral/tools/grid_fourier.F90 +atmos_spectral/tools/spec_mpp.F90 +atmos_spectral/tools/spherical.F90 +atmos_spectral/tools/spherical_fourier.F90 +atmos_spectral/tools/transforms.F90 +shared/constants/constants.F90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/tracer_manager/tracer_manager.F90 diff --git a/src/extra/model/shallow/path_names b/src/extra/model/shallow/path_names new file mode 100644 index 000000000..14f1c56f1 --- /dev/null +++ b/src/extra/model/shallow/path_names @@ -0,0 +1,138 @@ +atmos_solo/atmos_model.F90 +atmos_spectral/model/fv_advection.F90 +atmos_spectral/model/leapfrog.F90 +atmos_spectral/model/spectral_damping.F90 +atmos_spectral_shallow/atmosphere.F90 +atmos_spectral_shallow/shallow_diagnostics.F90 +atmos_spectral_shallow/shallow_dynamics.F90 +atmos_spectral_shallow/shallow_physics.F90 +atmos_spectral_barotropic/stirring.F90 +atmos_spectral/tools/gauss_and_legendre.F90 +atmos_spectral/tools/grid_fourier.F90 +atmos_spectral/tools/spec_mpp.F90 +atmos_spectral/tools/spherical.F90 +atmos_spectral/tools/spherical_fourier.F90 +atmos_spectral/tools/transforms.F90 +shared/constants/constants.F90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/tracer_manager/tracer_manager.F90 From e74e5c0d9c28ac7933570c711d74dc3a72f5638f Mon Sep 17 00:00:00 2001 From: sit23 Date: Fri, 17 May 2019 17:02:59 +0100 Subject: [PATCH 13/34] Added ability to do deep velocities, I think. Seems reasonable. Now testing. --- .../shallow_diagnostics.F90 | 4 +++- .../shallow_dynamics.F90 | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 index e0fd0052a..447cf64aa 100644 --- a/src/atmos_spectral_shallow/shallow_diagnostics.F90 +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -55,7 +55,7 @@ module shallow_diagnostics_mod logical :: module_is_initialized = .false. -integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot integer :: is, ie, js, je @@ -106,6 +106,7 @@ subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_l id_stream = register_diag_field(mod_name, 'stream', axis_2d, Time, 'streamfunction' , 'm^2/s' ) id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) +id_d_geopot = register_diag_field(mod_name, 'deep_geopot', axis_2d, Time, 'deep_geopot' , 'm2/s2') module_is_initialized = .true. @@ -132,6 +133,7 @@ subroutine shallow_diagnostics(Time, Grid, Phys, time_index) if(id_stream > 0) used = send_data(id_stream , Grid%stream (:,:) , time) if(id_tr > 0) used = send_data(id_tr , Grid%tr (:,:, time_index) , time) if(id_trs > 0) used = send_data(id_trs , Grid%trs (:,:, time_index) , time) +if(id_d_geopot > 0) used = send_data(id_d_geopot, Grid%deep_geopot (:,:) , time) return end subroutine shallow_diagnostics diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index eacbfc159..79946cf0a 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -39,7 +39,7 @@ module shallow_dynamics_mod operator(==), & operator(-) -use constants_mod, only : radius, omega +use constants_mod, only : radius, omega, DEG_TO_RAD use transforms_mod, only: transforms_init, transforms_end, & get_grid_boundaries, horizontal_advection, & @@ -76,7 +76,7 @@ module shallow_dynamics_mod type grid_type real, pointer, dimension(:,:,:) :: u=>NULL(), v=>NULL(), vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL(), tr=>NULL() - real, pointer, dimension(:,:) :: stream=>NULL(), pv=>NULL() + real, pointer, dimension(:,:) :: stream=>NULL(), pv=>NULL(), deep_geopot=>NULL() end type type spectral_type complex, pointer, dimension(:,:,:) :: vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL() @@ -132,6 +132,9 @@ module shallow_dynamics_mod real :: damping_coeff = 1.e-04 real :: h_0 = 3.e04 +real :: u_deep_mag = 0. +real :: n_merid_deep_flow = 3. + logical :: spec_tracer = .true. logical :: grid_tracer = .true. @@ -146,7 +149,8 @@ module shallow_dynamics_mod robert_coeff, robert_coeff_tracer, & h_0, spec_tracer, grid_tracer, & valid_range_v, cutoff_wn, & - raw_filter_coeff + raw_filter_coeff, & + u_deep_mag, n_merid_deep_flow contains @@ -235,6 +239,8 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) allocate (Dyn%tend%h (is:ie, js:je)) allocate (Dyn%grid%stream (is:ie, js:je)) allocate (Dyn%grid%pv (is:ie, js:je)) +allocate (Dyn%grid%deep_geopot(is:ie, js:je)) + call fv_advection_init(num_lon, num_lat, glat_bnd, 360./float(fourier_inc)) if(Dyn%grid_tracer) then @@ -248,6 +254,10 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) endif +do i = is, ie + Dyn%grid%deep_geopot(i, js:je) = -2.*omega * u_deep_mag * radius * (1./(1.-n_merid_deep_flow**2.))*(-cos(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*cos(DEG_TO_RAD*deg_lat(js:je)) - n_merid_deep_flow * (sin(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*sin(DEG_TO_RAD*deg_lat(js:je))-sin(n_merid_deep_flow*(2.*atan(1.))))) +enddo + if(Time == Time_init) then Dyn%Grid%vor(:,:,1) = 0.0 @@ -326,7 +336,7 @@ subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, del call trans_grid_to_spherical (Dyn%Tend%h, dt_hs) -bg = (Dyn%Grid%h(:,:,current) + & +bg = (Dyn%Grid%h(:,:,current) + Dyn%grid%deep_geopot(:,:) + & 0.5*(Dyn%Grid%u(:,:,current)**2 + Dyn%Grid%v(:,:,current)**2)) call trans_grid_to_spherical(bg, bs) @@ -370,7 +380,7 @@ subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, del stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) ! for diagnostic purposes call trans_spherical_to_grid(stream, Dyn%grid%stream) -Dyn%Grid%pv = vorg/Dyn%Grid%h(:,:,current) +Dyn%Grid%pv = vorg/(Dyn%Grid%h(:,:,current)+Dyn%grid%deep_geopot(:,:)) return end subroutine shallow_dynamics From 172087f14c844ee6c2e798d833757a1f1e5744fa Mon Sep 17 00:00:00 2001 From: sit23 Date: Fri, 31 May 2019 10:07:09 +0100 Subject: [PATCH 14/34] Updaing shallow water code to better account for deep velocities in ics and in PV definition, which I previously modified incorrectly. --- .../shallow_diagnostics.F90 | 45 +++++++++++++++++-- .../shallow_dynamics.F90 | 12 +++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 index 447cf64aa..ce485758b 100644 --- a/src/atmos_spectral_shallow/shallow_diagnostics.F90 +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -27,7 +27,8 @@ module shallow_diagnostics_mod get_deg_lat, & get_grid_domain, & get_spec_domain, & - grid_domain + grid_domain, & + area_weighted_global_mean use diag_manager_mod, only: diag_axis_init, & register_diag_field, & @@ -55,7 +56,7 @@ module shallow_diagnostics_mod logical :: module_is_initialized = .false. -integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin integer :: is, ie, js, je @@ -102,12 +103,22 @@ subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_l id_vor = register_diag_field(mod_name, 'vor' , axis_2d, Time, 'relative vorticity' , '1/s' ) id_div = register_diag_field(mod_name, 'div' , axis_2d, Time, 'divergence' , '1/s' ) id_h = register_diag_field(mod_name, 'h' , axis_2d, Time, 'geopotential' , 'm2/s2' ) -id_pv = register_diag_field(mod_name, 'pv' , axis_2d, Time, 'potential vorticity' , 's/m2' ) +id_pv = register_diag_field(mod_name, 'pv_corrected' , axis_2d, Time, 'potential vorticity' , 's/m2' ) id_stream = register_diag_field(mod_name, 'stream', axis_2d, Time, 'streamfunction' , 'm^2/s' ) id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) id_d_geopot = register_diag_field(mod_name, 'deep_geopot', axis_2d, Time, 'deep_geopot' , 'm2/s2') +id_u_sqd = register_diag_field(mod_name, 'ucomp_sqd' , axis_2d, Time, 'u_wind_sqd' , 'm^2/s^2' ) +id_v_sqd = register_diag_field(mod_name, 'vcomp_sqd' , axis_2d, Time, 'v_wind_sqd' , 'm^2/s^2' ) +id_h_sqd = register_diag_field(mod_name, 'h_sqd' , axis_2d, Time, 'geopotential_sqd' , 'm4/s4' ) + +id_u_sqd_mean = register_diag_field(mod_name, 'ucomp_sqd_mean' , Time, 'u_wind_sqd_mean' , 'm^2/s^2' ) +id_v_sqd_mean = register_diag_field(mod_name, 'vcomp_sqd_mean' , Time, 'v_wind_sqd_mean' , 'm^2/s^2' ) +id_h_sqd_mean = register_diag_field(mod_name, 'h_sqd_mean' , Time, 'geopotential_sqd_mean' , 'm4/s4' ) + +id_ekin = register_diag_field(mod_name, 'e_kin' , Time, 'kinetic_energy' , 'm^2/s^2' ) + module_is_initialized = .true. return @@ -135,6 +146,34 @@ subroutine shallow_diagnostics(Time, Grid, Phys, time_index) if(id_trs > 0) used = send_data(id_trs , Grid%trs (:,:, time_index) , time) if(id_d_geopot > 0) used = send_data(id_d_geopot, Grid%deep_geopot (:,:) , time) +if (id_u_sqd > 0) then + used = send_data(id_u_sqd , Grid%u (:,:, time_index)**2 , time) +endif + +if (id_v_sqd > 0) then + used = send_data(id_v_sqd , Grid%v (:,:, time_index)**2 , time) +endif + +if (id_h_sqd > 0) then + used = send_data(id_h_sqd , Grid%h (:,:, time_index)**2 , time) +endif + +if (id_u_sqd_mean > 0) then + used = send_data(id_u_sqd_mean , area_weighted_global_mean(Grid%u (:,:, time_index)**2) , time) +endif + +if (id_v_sqd_mean > 0) then + used = send_data(id_v_sqd_mean , area_weighted_global_mean(Grid%v (:,:, time_index)**2) , time) +endif + +if (id_h_sqd_mean > 0) then + used = send_data(id_h_sqd_mean , area_weighted_global_mean(Grid%h (:,:, time_index)**2) , time) +endif + +if (id_ekin > 0) then + used = send_data(id_ekin , 0.5*(area_weighted_global_mean(Grid%u (:,:, time_index)**2) + area_weighted_global_mean(Grid%v (:,:, time_index)**2)) , time) +endif + return end subroutine shallow_diagnostics !-------------------------------------------------------------------------------------------- diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 79946cf0a..8953ca662 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -49,7 +49,8 @@ module shallow_dynamics_mod get_deg_lon, get_deg_lat, & get_grid_domain, get_spec_domain, & spectral_domain, grid_domain, & - vor_div_from_uv_grid, uv_grid_from_vor_div + vor_div_from_uv_grid, uv_grid_from_vor_div, & + area_weighted_global_mean use spectral_damping_mod, only: spectral_damping_init, compute_spectral_damping @@ -165,7 +166,7 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) integer :: i, j real, allocatable, dimension(:) :: glon_bnd, glat_bnd -real :: xx, yy, dd +real :: xx, yy, dd, deep_geopot_global_mean integer :: ierr, io, unit, id_lon, id_lat, id_lonb, id_latb logical :: root @@ -258,11 +259,14 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) Dyn%grid%deep_geopot(i, js:je) = -2.*omega * u_deep_mag * radius * (1./(1.-n_merid_deep_flow**2.))*(-cos(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*cos(DEG_TO_RAD*deg_lat(js:je)) - n_merid_deep_flow * (sin(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*sin(DEG_TO_RAD*deg_lat(js:je))-sin(n_merid_deep_flow*(2.*atan(1.))))) enddo +deep_geopot_global_mean = area_weighted_global_mean(Dyn%grid%deep_geopot(:,:)) +Dyn%grid%deep_geopot(:,:) = Dyn%grid%deep_geopot(:,:)-deep_geopot_global_mean + if(Time == Time_init) then Dyn%Grid%vor(:,:,1) = 0.0 Dyn%Grid%div(:,:,1) = 0.0 - Dyn%Grid%h (:,:,1) = h_0 + Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) call trans_grid_to_spherical(Dyn%Grid%div(:,:,1), Dyn%Spec%div(:,:,1)) @@ -380,7 +384,7 @@ subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, del stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) ! for diagnostic purposes call trans_spherical_to_grid(stream, Dyn%grid%stream) -Dyn%Grid%pv = vorg/(Dyn%Grid%h(:,:,current)+Dyn%grid%deep_geopot(:,:)) +Dyn%Grid%pv = vorg/(Dyn%Grid%h(:,:,current)) return end subroutine shallow_dynamics From de1f769f32623a424ea193af9882b0a67cf73e46 Mon Sep 17 00:00:00 2001 From: sit23 Date: Fri, 31 May 2019 10:08:11 +0100 Subject: [PATCH 15/34] First lot of experiments for poster. Run at T170 and forced at small-ish scale. --- ...et_shallow_water_deep_velocity_test_mk1.py | 166 +++++++++++++++++ ...ter_deep_velocity_test_mk1_vary_damping.py | 169 +++++++++++++++++ ...et_shallow_water_deep_velocity_test_mk1.py | 167 +++++++++++++++++ ...et_shallow_water_deep_velocity_test_mk2.py | 170 ++++++++++++++++++ ...shallow_water_small_scale_stirring_test.py | 14 +- 5 files changed, 679 insertions(+), 7 deletions(-) create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py new file mode 100644 index 000000000..56d419739 --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py @@ -0,0 +1,166 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) +diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for ld_value in [10.0, 0.1, 0.05, 0.01]: + + for u_deep_mag_val in [50., 0.]: + + if u_deep_mag_val!=0.: + u_deep_merid_arr = [3, 11, 19, 26] + else: + u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + exp = Experiment('giant_planet_fixed_deep_ics_forced_no_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * 0., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13, + 'n_total_forcing_max': 85, + 'n_total_forcing_min': 79, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,361): + exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py new file mode 100644 index 000000000..c2335a0be --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py @@ -0,0 +1,169 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv_correct', time_avg=True) + +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) +diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for ld_value in [0.1]: + + for u_deep_mag_val in [0., 50.]: + + if u_deep_mag_val!=0.: + u_deep_merid_arr = [3, 11, 19, 26] + else: + u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + for damping_time_rot in [1000., 10000.]: #, 0., 100., Also want to do these, but can alias existing runs for them + + exp = Experiment('giant_planet_fixed_deep_ics_forced_rad_damping_'+str(damping_time_rot)+'_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * damping_time_rot, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13, + 'n_total_forcing_max': 85, + 'n_total_forcing_min': 79, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,721): + exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py new file mode 100644 index 000000000..0d5ce04b4 --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py @@ -0,0 +1,167 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) + + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for ld_value in [10.0]: + for u_deep_mag_val in [50.,]: + #for u_deep_mag_val in [-50.]: + + if u_deep_mag_val!=0.: + u_deep_merid_arr = [3,] + else: + u_deep_merid_arr = [3] + + #u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + exp = Experiment('giant_planet_shallow_water_deep_velocity_test_mk2_no_forcing_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * 100., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':0., + 'n_total_forcing_max': 85, + 'n_total_forcing_min': 79, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,2101): + exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py new file mode 100644 index 000000000..711b2c34b --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py @@ -0,0 +1,170 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') +# diag.add_file('atmos_timestep', 1200, 'seconds', time_units='days') + + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) + + + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 512, + 'num_lat' : 256, + 'num_fourier' : 170, + 'num_spherical' : 171, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for ld_value in [0.1, 10.0]: + # for u_deep_mag_val in [50., 0., 25.]: + for u_deep_mag_val in [50., 0.]: + + # if u_deep_mag_val!=0.: + # u_deep_merid_arr = [3, 11, 19, 26] + # else: + # u_deep_merid_arr = [3] + + u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + exp = Experiment('giant_planet_shallow_water_deep_velocity_test_mk8_no_forcing_no_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : 0. + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':0., + 'n_total_forcing_max': 85, + 'n_total_forcing_min': 79, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py b/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py index a45007b8b..14c4b323c 100644 --- a/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py +++ b/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py @@ -6,7 +6,7 @@ import pdb -NCORES = 8 +NCORES = 16 base_dir = os.path.dirname(os.path.realpath(__file__)) # a CodeBase can be a directory on the computer, # useful for iterative development @@ -106,9 +106,9 @@ #Lets do a run! if __name__=="__main__": - for number_ld_in_radius_units in [10.]: + for number_ld_in_radius_units in [0.1]: - exp = Experiment('giant_shallow_stirring_test_experiment_uniform_small_scale_stirring_mk3', codebase=cb) + exp = Experiment('giant_shallow_stirring_test_experiment_uniform_smaller_120_scale_stirring_mk3_longer_damping_ld_'+str(number_ld_in_radius_units), codebase=cb) exp.diag_table = diag exp.namelist = namelist @@ -130,7 +130,7 @@ }, 'shallow_physics_nml': { 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * 1., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + 'therm_damp_time' : rotation_period * 10000., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) }, 'constants_nml': { 'omega': omega, @@ -139,13 +139,13 @@ 'stirring_nml': { 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. 'amplitude':3.e-13, - 'n_total_forcing_max': 45, - 'n_total_forcing_min': 39, + 'n_total_forcing_max': 125, + 'n_total_forcing_min': 119, }, }) exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,122): + for i in range(2,121): exp.run(i, num_cores=NCORES) From c09ad56ad00ceb7c6cc1d259a68edc2054347d8f Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 18 Jun 2019 19:05:55 +0100 Subject: [PATCH 16/34] Updating outputs for energy diagnostics. --- ...et_shallow_water_deep_velocity_test_mk1.py | 23 ++++-- .../shallow_diagnostics.F90 | 72 ++++++++++++++++++- .../shallow_dynamics.F90 | 9 ++- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py index 56d419739..fe65deba8 100644 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py @@ -49,6 +49,14 @@ diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) +diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) + + #Empty the run directory ready to run #Define values for the 'core' namelist @@ -109,18 +117,22 @@ #Lets do a run! if __name__=="__main__": - for ld_value in [10.0, 0.1, 0.05, 0.01]: + # for ld_value in [10.0, 0.1, 0.05, 0.01]: + for ld_value in [0.05,]: - for u_deep_mag_val in [50., 0.]: + + for u_deep_mag_val in [50.]: if u_deep_mag_val!=0.: - u_deep_merid_arr = [3, 11, 19, 26] + u_deep_merid_arr = [27] + # u_deep_merid_arr = [5,7,9] + else: u_deep_merid_arr = [3] for u_deep_merid in u_deep_merid_arr: - exp = Experiment('giant_planet_fixed_deep_ics_forced_no_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) + exp = Experiment('giant_planet_fixed_deep_ics_forced_no_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_energy_outputs_3', codebase=cb) exp.diag_table = diag exp.namelist = namelist @@ -141,7 +153,8 @@ 'shallow_dynamics_nml':{ 'h_0': equilibrium_geopotential, 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, + 'n_merid_deep_flow': u_deep_merid, + # 'u_upper_mag_init': u_deep_mag_val, }, 'shallow_physics_nml': { 'h_0': equilibrium_geopotential, diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 index ce485758b..7a8cbed09 100644 --- a/src/atmos_spectral_shallow/shallow_diagnostics.F90 +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -56,7 +56,7 @@ module shallow_diagnostics_mod logical :: module_is_initialized = .false. -integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin, id_ekin_density, id_eq_geopot, id_e_kin_real_units, id_e_pot_real_units, id_e_tot_real_units, id_u_rms integer :: is, ie, js, je @@ -118,6 +118,14 @@ subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_l id_h_sqd_mean = register_diag_field(mod_name, 'h_sqd_mean' , Time, 'geopotential_sqd_mean' , 'm4/s4' ) id_ekin = register_diag_field(mod_name, 'e_kin' , Time, 'kinetic_energy' , 'm^2/s^2' ) +id_ekin_density = register_diag_field(mod_name, 'e_kin_density' , Time, 'kinetic_energy_density' , 'm^3/s^2' ) + +id_eq_geopot = register_diag_field(mod_name, 'eq_geopot' , Time, 'equilibrium_geopotential' , 'm^2/s^2' ) +id_e_kin_real_units = register_diag_field(mod_name, 'e_kin_real_units' , Time, 'e_kin_real_units' , 'J/kg' ) +id_e_pot_real_units = register_diag_field(mod_name, 'e_pot_real_units' , Time, 'e_pot_real_units' , 'J/kg' ) +id_e_tot_real_units = register_diag_field(mod_name, 'e_tot_real_units' , Time, 'e_tot_real_units' , 'J/kg' ) + +id_u_rms = register_diag_field(mod_name, 'u_rms' , Time, 'r_rms' , 'm/s' ) module_is_initialized = .true. @@ -133,6 +141,8 @@ subroutine shallow_diagnostics(Time, Grid, Phys, time_index) type(grid_type), intent(in) :: Grid integer, intent(in) :: time_index +real :: e_kin_real_units, e_pot_real_units, e_tot_real_units, eq_geopot + logical :: used if(id_u > 0) used = send_data(id_u , Grid%u (:,:, time_index) , time) @@ -174,6 +184,66 @@ subroutine shallow_diagnostics(Time, Grid, Phys, time_index) used = send_data(id_ekin , 0.5*(area_weighted_global_mean(Grid%u (:,:, time_index)**2) + area_weighted_global_mean(Grid%v (:,:, time_index)**2)) , time) endif +if (id_ekin_density > 0) then + used = send_data(id_ekin_density , 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) , time) +endif + +eq_geopot = 0. +e_kin_real_units = 0. +e_pot_real_units = 0. + +if (id_eq_geopot > 0) then + + eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + used = send_data(id_eq_geopot , eq_geopot, time) + +endif + + +if (id_e_kin_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + e_kin_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) / eq_geopot + + used = send_data(id_e_kin_real_units , e_kin_real_units, time) + +endif + +if (id_e_pot_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + e_pot_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)**2.)) / eq_geopot + + used = send_data(id_e_pot_real_units , e_pot_real_units, time) + +endif + +if (id_e_tot_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + if (e_kin_real_units == 0.) then + e_kin_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) / eq_geopot + endif + + if (e_pot_real_units == 0.) then + e_pot_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)**2.)) / eq_geopot + endif + + e_tot_real_units = e_kin_real_units + e_pot_real_units + + used = send_data(id_e_tot_real_units , e_tot_real_units, time) + +endif + +if (id_u_rms > 0) then + + used = send_data(id_u_rms , (area_weighted_global_mean(Grid%u (:,:, time_index)**2) + area_weighted_global_mean(Grid%v (:,:, time_index)**2))**0.5, time) + +endif + return end subroutine shallow_diagnostics !-------------------------------------------------------------------------------------------- diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 8953ca662..67ce5b3e8 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -135,6 +135,7 @@ module shallow_dynamics_mod real :: u_deep_mag = 0. real :: n_merid_deep_flow = 3. +real :: u_upper_mag_init = 0. logical :: spec_tracer = .true. logical :: grid_tracer = .true. @@ -151,7 +152,8 @@ module shallow_dynamics_mod h_0, spec_tracer, grid_tracer, & valid_range_v, cutoff_wn, & raw_filter_coeff, & - u_deep_mag, n_merid_deep_flow + u_deep_mag, n_merid_deep_flow, & + u_upper_mag_init contains @@ -264,7 +266,10 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) if(Time == Time_init) then - Dyn%Grid%vor(:,:,1) = 0.0 + do i = is, ie + Dyn%Grid%vor(i,js:je,1) = -((u_upper_mag_init * n_merid_deep_flow)/radius) * sin(DEG_to_RAD * deg_lat(js:je)) + enddo + Dyn%Grid%div(:,:,1) = 0.0 Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) From c1a3f2af8c6d75cb92a2fd7277a62ae34e94a5fe Mon Sep 17 00:00:00 2001 From: sit23 Date: Sat, 29 Jun 2019 22:21:21 +0100 Subject: [PATCH 17/34] Updated experiments. --- ...w_water_deep_velocity_test_mk1_high_res.py | 175 ++++++++++++++++++ ...w_water_deep_velocity_test_mk1_high_res.py | 175 ++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py create mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py new file mode 100644 index 000000000..5772107da --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py @@ -0,0 +1,175 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 32 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) +diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) + +diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) +diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 600, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 1024, + 'num_lat' : 512, + 'num_fourier' : 341, + 'num_spherical' : 342, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for forcing_amplitude in [7.5, 5., 10.]: + + for damping_time in [ 10000., 1000., 100000.]: + u_deep_mag_val = 50. + + if u_deep_mag_val!=0.: + u_deep_merid_arr = [27] + else: + u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + ld_value = 0.025 + exp = Experiment('high_res_small_forcing_giant_planet_fixed_deep_ics_forced_'+str(damping_time)+'_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_strong_forcing_'+str(forcing_amplitude), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * damping_time, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13*forcing_amplitude, + 'n_total_forcing_max': 170, + 'n_total_forcing_min': 164, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES, multi_node=True) + for i in range(2,721): + exp.run(i, num_cores=NCORES, multi_node=True) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py new file mode 100644 index 000000000..2c6a8f028 --- /dev/null +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py @@ -0,0 +1,175 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +import pdb + +NCORES = 32 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +# diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) +diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) + +diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) +diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) +diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) + +#Empty the run directory ready to run + +#Define values for the 'core' namelist +namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 600, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':False, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 1024, + 'num_lat' : 512, + 'num_fourier' : 341, + 'num_spherical' : 342, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : False, + 'spec_tracer' : False, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. + 'h_amp' : 0., + 'h_itcz' : 0., + }, + + 'stirring_nml': { + 'B':0.0, + 'do_localize': False, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + for forcing_amplitude in [ 10.]: + + for damping_time in [ 10000., 1000., 100000.]: + u_deep_mag_val = 50. + + if u_deep_mag_val!=0.: + u_deep_merid_arr = [27] + else: + u_deep_merid_arr = [3] + + for u_deep_merid in u_deep_merid_arr: + + ld_value = 0.025 + exp = Experiment('high_res_small_forcing_giant_planet_fixed_deep_ics_forced_'+str(damping_time)+'_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_strong_forcing_'+str(forcing_amplitude), codebase=cb) + + exp.diag_table = diag + exp.namelist = namelist + exp.clear_rundir() + + rotation_period = ((9.*3600.)+55.*60 + 30.) + omega = 2.*np.pi/ rotation_period + radius = 69911e3 + grav = 24.79 + number_ld_in_radius_units = ld_value + + # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. + + equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. + equilibrium_depth = equilibrium_geopotential/grav + + exp.update_namelist({ + 'shallow_dynamics_nml':{ + 'h_0': equilibrium_geopotential, + 'u_deep_mag' : u_deep_mag_val, + 'n_merid_deep_flow': u_deep_merid, + }, + 'shallow_physics_nml': { + 'h_0': equilibrium_geopotential, + 'therm_damp_time' : rotation_period * damping_time, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) + }, + 'constants_nml': { + 'omega': omega, + 'radius': radius, + }, + 'stirring_nml': { + 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. + 'amplitude':3.e-13*forcing_amplitude, + 'n_total_forcing_max': 170, + 'n_total_forcing_min': 164, + + }, + + }) + + exp.run(1, use_restart=False, num_cores=NCORES, multi_node=True) + for i in range(2,721): + exp.run(i, num_cores=NCORES, multi_node=True) From 4e46f2ce6011e952f4eb441b41040964822ea94e Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 2 Jul 2019 14:43:47 +0100 Subject: [PATCH 18/34] Ignorning supercomputer log files. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 89b27824a..a414eda46 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,6 @@ src/extra/python/scripts/*.nc src/extra/python/scripts/archived/ mima_pz.txt test/.cache -test/results.xml \ No newline at end of file +test/results.xml +*.sh.e* +*.sh.o* From f1cc51d60631154c87ee718be4d0f4dd6c2668d3 Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 2 Jul 2019 14:54:52 +0100 Subject: [PATCH 19/34] Adding diagnostics for momentum budget. --- src/atmos_spectral_shallow/shallow_diagnostics.F90 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 index 7a8cbed09..3ea5dc0a8 100644 --- a/src/atmos_spectral_shallow/shallow_diagnostics.F90 +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -56,7 +56,7 @@ module shallow_diagnostics_mod logical :: module_is_initialized = .false. -integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin, id_ekin_density, id_eq_geopot, id_e_kin_real_units, id_e_pot_real_units, id_e_tot_real_units, id_u_rms +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin, id_ekin_density, id_eq_geopot, id_e_kin_real_units, id_e_pot_real_units, id_e_tot_real_units, id_u_rms, id_vcomp_vor, id_ucomp_vcomp integer :: is, ie, js, je @@ -127,6 +127,10 @@ subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_l id_u_rms = register_diag_field(mod_name, 'u_rms' , Time, 'r_rms' , 'm/s' ) +id_vcomp_vor = register_diag_field(mod_name, 'vcomp_vor' , axis_2d, Time, 'vcomp * relative vorticity' , 'm/s^2' ) + +id_ucomp_vcomp = register_diag_field(mod_name, 'ucomp_vcomp' , axis_2d, Time, 'ucomp * vcomp' , 'm^2/s^2' ) + module_is_initialized = .true. return @@ -244,6 +248,10 @@ subroutine shallow_diagnostics(Time, Grid, Phys, time_index) endif + +if(id_vcomp_vor > 0) used = send_data(id_vcomp_vor , Grid%v (:,:, time_index) * Grid%vor (:,:, time_index) , time) +if(id_ucomp_vcomp > 0) used = send_data(id_ucomp_vcomp , Grid%u (:,:, time_index) * Grid%v (:,:, time_index) , time) + return end subroutine shallow_diagnostics !-------------------------------------------------------------------------------------------- From ee7394d7b9283eb141742b05e07a0f3b48752740 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 18 Jul 2019 15:06:45 +0100 Subject: [PATCH 20/34] Added daily output for more useful diagnostics. --- ...t_planet_shallow_water_deep_velocity_test_mk1_high_res.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py index 5772107da..b512fec67 100644 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py @@ -29,7 +29,7 @@ #Tell model how to write diagnostics diag = DiagTable() diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') +#diag.add_file('atmos_daily', 1, 'days', time_units='days') #Tell model which diagnostics to write diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) @@ -41,6 +41,9 @@ diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) diag.add_field('shallow_diagnostics', 'stream', time_avg=True) diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp_vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'ucomp_vcomp', time_avg=True) + # diag.add_field('shallow_diagnostics', 'trs', time_avg=True) # diag.add_field('shallow_diagnostics', 'tr', time_avg=True) diag.add_field('stirring_mod', 'stirring', time_avg=True) From bee23e085fe10c0fa4ae494563069410b3af592a Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 18 Jul 2019 15:09:17 +0100 Subject: [PATCH 21/34] Adding momentum budget analysis outputs. --- .../giant_planet_shallow_water_deep_velocity_test_mk1.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py index fe65deba8..40c853e40 100644 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py @@ -41,6 +41,9 @@ diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) diag.add_field('shallow_diagnostics', 'stream', time_avg=True) diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp_vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'ucomp_vcomp', time_avg=True) + # diag.add_field('shallow_diagnostics', 'trs', time_avg=True) # diag.add_field('shallow_diagnostics', 'tr', time_avg=True) diag.add_field('stirring_mod', 'stirring', time_avg=True) @@ -124,7 +127,7 @@ for u_deep_mag_val in [50.]: if u_deep_mag_val!=0.: - u_deep_merid_arr = [27] + u_deep_merid_arr = [23] # u_deep_merid_arr = [5,7,9] else: @@ -175,5 +178,5 @@ }) exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,361): + for i in range(2,481): exp.run(i, num_cores=NCORES) From cbca8c77abe3adb65ce09ff479a1b93b6e8eb5a3 Mon Sep 17 00:00:00 2001 From: sit23 Date: Fri, 7 Feb 2020 09:11:16 +0000 Subject: [PATCH 22/34] parameter updates. --- ..._planet_shallow_water_deep_velocity_test_mk1_high_res.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py index b512fec67..c7af1d59c 100644 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py +++ b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py @@ -119,9 +119,9 @@ #Lets do a run! if __name__=="__main__": - for forcing_amplitude in [7.5, 5., 10.]: + for forcing_amplitude in [7.5]: - for damping_time in [ 10000., 1000., 100000.]: + for damping_time in [ 100000.]: u_deep_mag_val = 50. if u_deep_mag_val!=0.: @@ -174,5 +174,5 @@ }) exp.run(1, use_restart=False, num_cores=NCORES, multi_node=True) - for i in range(2,721): + for i in range(721,841): exp.run(i, num_cores=NCORES, multi_node=True) From bb15cef1f0443c852a0cccec72e230e434ec6460 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 19 Mar 2020 08:49:02 +0000 Subject: [PATCH 23/34] Update shallow code to include vortex migration experiments. --- .../scripts/run_plevel.py | 17 +-- .../shallow_dynamics.F90 | 65 +++++++++- .../remove_certain_restart_and_data_files.py | 111 ++++++++++-------- 3 files changed, 130 insertions(+), 63 deletions(-) diff --git a/postprocessing/plevel_interpolation/scripts/run_plevel.py b/postprocessing/plevel_interpolation/scripts/run_plevel.py index 294ecb53a..6b6b9bfe7 100644 --- a/postprocessing/plevel_interpolation/scripts/run_plevel.py +++ b/postprocessing/plevel_interpolation/scripts/run_plevel.py @@ -7,11 +7,12 @@ import subprocess start_time=time.time() -base_dir='/scratch/sit204/Data_2013/' -exp_name_list = ['no_ice_flux_lhe_exps_q_flux_hadgem_anoms_3'] +base_dir='/disca/share/sit204/data_from_isca_cpu/cssp_perturb_exps/anoms/' +#exp_name_list = ['soc_ga3_files_smooth_topo_fftw_mk1_fresh_compile_long', 'soc_ga3_files_smooth_topo_old_fft_mk2_long'] +exp_name_list = [f'soc_ga3_do_simple_false_cmip_o3_bucket_perturbed_ens_{f}' for f in range(100, 200)] avg_or_daily_list=['monthly'] -start_file=287 -end_file=288 +start_file=1 +end_file=1 nfiles=(end_file-start_file)+1 do_extra_averaging=False #If true, then 6hourly data is averaged into daily data using cdo @@ -44,7 +45,7 @@ var_names['timestep']='-a' var_names['6hourly']='ucomp slp height vor t_surf vcomp omega' var_names['daily']='ucomp slp height vor t_surf vcomp omega temp' - file_suffix='_interp_new_height_temp' + file_suffix='_interp_new_height_temp_not_below_ground' elif level_set=='ssw_diagnostics': plevs['6hourly']=' -p "1000 10000"' @@ -66,10 +67,12 @@ number_prefix='' - if n+start_file < 100: + if n+start_file < 1000: number_prefix='0' - if n+start_file < 10: + if n+start_file < 100: number_prefix='00' + if n+start_file < 10: + number_prefix = '000' nc_file_in = base_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+'.nc' nc_file_out = out_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+file_suffix+'.nc' diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 67ce5b3e8..7ba1ec3f8 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -140,6 +140,18 @@ module shallow_dynamics_mod logical :: spec_tracer = .true. logical :: grid_tracer = .true. +!Options for injecting an initial vortex pair +real :: lon_centre_init_cyc = 0. +real :: lat_centre_init_cyc = 60. +real :: lon_centre_init_acyc = 180. +real :: lat_centre_init_acyc = 60. +real :: init_vortex_radius_deg = 5. +real :: init_vortex_vor_f = 0.5 +real :: init_vortex_h_h_0 = 0.1 +logical :: add_initial_vortex_pair = .false. +logical :: add_initial_vortex_as_height = .true. + + real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) namelist /shallow_dynamics_nml/ check_fourier_imag, & @@ -153,7 +165,17 @@ module shallow_dynamics_mod valid_range_v, cutoff_wn, & raw_filter_coeff, & u_deep_mag, n_merid_deep_flow, & - u_upper_mag_init + u_upper_mag_init, & + lon_centre_init_cyc, & + lat_centre_init_cyc, & + lon_centre_init_acyc, & + lat_centre_init_acyc, & + init_vortex_radius_deg, & + init_vortex_vor_f, & + init_vortex_h_h_0, & + add_initial_vortex_pair, & + add_initial_vortex_as_height + contains @@ -168,7 +190,7 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) integer :: i, j real, allocatable, dimension(:) :: glon_bnd, glat_bnd -real :: xx, yy, dd, deep_geopot_global_mean +real :: xx, yy, dd, deep_geopot_global_mean, radius_loc_cyc, radius_loc_acyc integer :: ierr, io, unit, id_lon, id_lat, id_lonb, id_latb logical :: root @@ -266,12 +288,45 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) if(Time == Time_init) then + Dyn%Grid%div(:,:,1) = 0.0 + Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) + do i = is, ie Dyn%Grid%vor(i,js:je,1) = -((u_upper_mag_init * n_merid_deep_flow)/radius) * sin(DEG_to_RAD * deg_lat(js:je)) - enddo - Dyn%Grid%div(:,:,1) = 0.0 - Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) + if (add_initial_vortex_pair) then + + do j=js, je + + radius_loc_cyc = ((min((deg_lon(i)-lon_centre_init_cyc)**2., (deg_lon(i)-lon_centre_init_cyc-360.)**2.)+(deg_lat(j)-lat_centre_init_cyc)**2.)**0.5)/init_vortex_radius_deg + radius_loc_acyc = ((min((deg_lon(i)-lon_centre_init_acyc)**2., (deg_lon(i)-lon_centre_init_acyc-360.)**2.)+(deg_lat(j)-lat_centre_init_acyc)**2.)**0.5)/init_vortex_radius_deg + + + if(radius_loc_cyc.le.1.0 .and. radius_loc_acyc.le.1.0) then + call error_mesg('shallow_dynamics','Cannot initialise cyclone and anticyclone in same grid box ', FATAL) + endif + + if(add_initial_vortex_as_height) then + if (radius_loc_cyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * -h_0 * exp(-radius_loc_cyc**2.) + elseif (radius_loc_acyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * h_0 * exp(-radius_loc_acyc**2.) + endif + else + if (radius_loc_cyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * 2.*omega + elseif (radius_loc_acyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * -2.*omega + endif + + endif + + enddo + + + endif + + enddo call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) call trans_grid_to_spherical(Dyn%Grid%div(:,:,1), Dyn%Spec%div(:,:,1)) diff --git a/src/extra/python/scripts/remove_certain_restart_and_data_files.py b/src/extra/python/scripts/remove_certain_restart_and_data_files.py index 98d658a95..582a05c9c 100644 --- a/src/extra/python/scripts/remove_certain_restart_and_data_files.py +++ b/src/extra/python/scripts/remove_certain_restart_and_data_files.py @@ -1,6 +1,7 @@ import sh import os import pdb +from glob import glob P = os.path.join @@ -12,11 +13,15 @@ def __init__(self, basedir, workdir, datadir, exp_name): self.expname = exp_name -def create_exp_object(exp_name): +def create_exp_object(exp_name, data_directory=None): + + if data_directory is None: + datadir = os.environ['GFDL_DATA'] + else: + datadir = data_directory - basedir = os.environ['GFDL_BASE'] workdir = os.environ['GFDL_WORK'] - datadir = os.environ['GFDL_DATA'] + basedir = os.environ['GFDL_BASE'] expname = '/'+exp_name+'/' exp_object = temporary_exp_object(basedir, workdir, datadir, exp_name) @@ -24,34 +29,42 @@ def create_exp_object(exp_name): return exp_object -def keep_only_certain_restart_files(exp_object, max_num_files, interval=12): +# def keep_only_certain_restart_files(exp_object, max_num_files, interval=12): - # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. +# # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. - #First defines a list of ALL the restart file numbers - files_to_remove=list(range(0,max_num_files)) +# #First defines a list of ALL the restart file numbers +# files_to_remove=list(range(0,max_num_files)) - #Then defines a list of the ones we want to KEEP - files_to_keep =list(range(0,max_num_files,interval)) +# #Then defines a list of the ones we want to KEEP +# files_to_keep =list(range(0,max_num_files,interval)) - #Then we remove the files we want to keep from the list of all files, giving a list of those we wish to remove - for x in files_to_keep: - files_to_remove.remove(x) +# #Then we remove the files we want to keep from the list of all files, giving a list of those we wish to remove +# for x in files_to_keep: +# files_to_remove.remove(x) - #Then we remove them. - for entry in files_to_remove: - try: - sh.rm(P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio')) -# print P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio') +# #Then we remove them. +# for entry in files_to_remove: +# try: +# sh.rm(P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio')) +# # print P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio') - except sh.ErrorReturnCode_1: - pass -# print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' +# except sh.ErrorReturnCode_1: +# pass +# # print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' -def keep_only_certain_restart_files_data_dir(exp_object, max_num_files, interval=12): +def keep_only_certain_restart_files_data_dir(exp_object, max_num_files=None, interval=12): # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. + if max_num_files is None: + month_list = glob(P(exp_object.datadir,exp_object.expname, 'restarts')+'/res*.tar.gz') + if len(month_list)==0: + return + else: + final_month = month_list[-1].split('/res') + max_num_files = int(final_month[-1].split('.tar.gz')[0]) + #First defines a list of ALL the restart file numbers files_to_remove=list(range(0,max_num_files)) @@ -62,13 +75,27 @@ def keep_only_certain_restart_files_data_dir(exp_object, max_num_files, interval for x in files_to_keep: files_to_remove.remove(x) + first_to_be_removed = True + number_removed = 0 + number_not_removed = 0 #Then we remove them. - for entry in files_to_remove: + for entry in files_to_remove[1:-1]: try: - sh.rm(P(exp_object.datadir,exp_object.expname,'run%03d' % entry,'INPUT','res')) -# print 'would be removing ' + P(exp_object.datadir,exp_object.expname,'run'+str(entry),'INPUT','res') + file_to_remove = P(exp_object.datadir,exp_object.expname, 'restarts', 'res%04d.tar.gz' % entry) + if os.path.isfile(file_to_remove) and first_to_be_removed: + first_to_be_removed=False + number_not_removed+=1 + # print('would have removed '+file_to_remove+' but wanted to make sure not to delete the first restart') + else: + sh.rm(file_to_remove) + number_removed+=1 + # print('have removed ' + file_to_remove) except sh.ErrorReturnCode_1: + number_not_removed+=1 + # print('could not remove ' + file_to_remove) pass + print(P(exp_object.datadir,exp_object.expname), 'number removed '+str(number_removed), 'number not removed '+str(number_not_removed)) + # print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=None, file_name = 'atmos_daily.nc'): @@ -88,8 +115,8 @@ def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=No #Then we remove them. for entry in files_to_remove: try: - sh.rm(P(exp_object.datadir,exp_object.expname,'run%03d' % entry,file_name)) - print(('Removed '+P(exp_object.datadir,exp_object.expname,'run%03d' % entry,file_name))) + sh.rm(P(exp_object.datadir,exp_object.expname,'run%04d' % entry,file_name)) + print(('Removed '+P(exp_object.datadir,exp_object.expname,'run%04d' % entry,file_name))) except sh.ErrorReturnCode_1: pass # print 'Tried to remove some atmos_daily files, but number '+str(entry)+' does not exist' @@ -98,36 +125,18 @@ def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=No if __name__=="__main__": - max_num_files_input = 325 + max_num_files_input = None -# exp_name_list=['simple_continents_post_princeton_qflux_anoms_'+str(x) for x in range(31,32)] - -# exp_name_list=['aquaplanet_qflux_anoms_'+str(x) for x in [12,18,23,32,8]] - -# exp_name_list = ['simple_continents_post_princeton_qflux_control_1','simple_continents_post_princeton_fixed_sst_1', 'simple_continents_post_princeton_qflux_control_nod_1', 'simple_continents_post_princeton_qflux_control_scf_1'] -# -# exp_name_list = ['annual_mean_ice_princeton_qflux_control_matrix_qflux_2017_code_1', 'annual_mean_ice_post_princeton_fixed_sst_1', 'annual_mean_ice_princeton_fixed_sst_1'] -# -# exp_name_list.extend(['annual_mean_ice_post_princeton_fixed_sst_el_nino_1']) - -# exp_name_list = ['simple_continents_post_princeton_qflux_control_1'] - -# exp_name_list = ['annual_mean_ice_princeton_qflux_control_1']#, 'annual_mean_ice_post_princeton_qflux_control_1'] - -# exp_name_list = ['annual_mean_ice_post_princeton_fixed_sst_TEST_1', 'annual_mean_ice_princeton_qflux_control_matrix_qflux_1'] - -# exp_name_list.extend(['simple_continents_post_princeton_fixed_sst_1']) - -# exp_name_list = ['giant_drag_exp_chai_values_without_dc_bug_latest_1'] -# exp_name_list = ['aquaplanet_qflux_control_1'] + # exp_name_list = [''] + exp_name_list = glob('/disca/share/sit204/data_isca_from_gv5/frierson_post_soc_fix_*/') - exp_name_list = ['giant_drag_exp_chai_values_with_dc_bug_latest_start_to_finish_1', 'giant_drag_exp_chai_values_without_dc_bug_latest_start_to_finish_1'] for exp_name_input in exp_name_list: - temp_obj = create_exp_object(exp_name_input) - keep_only_certain_restart_files(temp_obj, max_num_files_input) + print('Percentage progress through list:'+str(exp_name_list.index(exp_name_input)/len(exp_name_list))) + temp_obj = create_exp_object(exp_name_input, data_directory='/disca/share/sit204/data_from_isca_cpu/') + # keep_only_certain_restart_files(temp_obj, max_num_files_input) keep_only_certain_restart_files_data_dir(temp_obj, max_num_files_input) - keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input, file_name = 'fms_moist.x') + # keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input, file_name = 'fms_moist.x') # keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input) \ No newline at end of file From d706b3407f103732c818c010f4abf447c6614efc Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 10 Dec 2020 17:53:17 +0000 Subject: [PATCH 24/34] Removing unecessary experiment files. --- ...et_shallow_water_deep_velocity_test_mk1.py | 182 ------------------ ...ter_deep_velocity_test_mk1_vary_damping.py | 169 ---------------- ...w_water_deep_velocity_test_mk1_high_res.py | 178 ----------------- ...w_water_deep_velocity_test_mk1_high_res.py | 175 ----------------- ...et_shallow_water_deep_velocity_test_mk1.py | 167 ---------------- ...et_shallow_water_deep_velocity_test_mk2.py | 170 ---------------- ...shallow_water_small_scale_stirring_test.py | 151 --------------- ...iant_planet_shallow_water_stirring_test.py | 148 -------------- 8 files changed, 1340 deletions(-) delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py delete mode 100644 exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py delete mode 100644 exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py delete mode 100644 exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py deleted file mode 100644 index 40c853e40..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1.py +++ /dev/null @@ -1,182 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 16 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp_vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'ucomp_vcomp', time_avg=True) - -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) -diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) - -diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) -diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) - - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - # for ld_value in [10.0, 0.1, 0.05, 0.01]: - for ld_value in [0.05,]: - - - for u_deep_mag_val in [50.]: - - if u_deep_mag_val!=0.: - u_deep_merid_arr = [23] - # u_deep_merid_arr = [5,7,9] - - else: - u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - exp = Experiment('giant_planet_fixed_deep_ics_forced_no_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_energy_outputs_3', codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - # 'u_upper_mag_init': u_deep_mag_val, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * 0., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13, - 'n_total_forcing_max': 85, - 'n_total_forcing_min': 79, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,481): - exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py deleted file mode 100644 index c2335a0be..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/giant_planet_shallow_water_deep_velocity_test_mk1_vary_damping.py +++ /dev/null @@ -1,169 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 16 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv_correct', time_avg=True) - -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) -diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for ld_value in [0.1]: - - for u_deep_mag_val in [0., 50.]: - - if u_deep_mag_val!=0.: - u_deep_merid_arr = [3, 11, 19, 26] - else: - u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - for damping_time_rot in [1000., 10000.]: #, 0., 100., Also want to do these, but can alias existing runs for them - - exp = Experiment('giant_planet_fixed_deep_ics_forced_rad_damping_'+str(damping_time_rot)+'_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * damping_time_rot, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13, - 'n_total_forcing_max': 85, - 'n_total_forcing_min': 79, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,721): - exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py deleted file mode 100644 index c7af1d59c..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py +++ /dev/null @@ -1,178 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 32 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -#diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp_vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'ucomp_vcomp', time_avg=True) - -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) -diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) - -diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) -diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 600, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 1024, - 'num_lat' : 512, - 'num_fourier' : 341, - 'num_spherical' : 342, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for forcing_amplitude in [7.5]: - - for damping_time in [ 100000.]: - u_deep_mag_val = 50. - - if u_deep_mag_val!=0.: - u_deep_merid_arr = [27] - else: - u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - ld_value = 0.025 - exp = Experiment('high_res_small_forcing_giant_planet_fixed_deep_ics_forced_'+str(damping_time)+'_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_strong_forcing_'+str(forcing_amplitude), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * damping_time, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13*forcing_amplitude, - 'n_total_forcing_max': 170, - 'n_total_forcing_min': 164, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES, multi_node=True) - for i in range(721,841): - exp.run(i, num_cores=NCORES, multi_node=True) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py deleted file mode 100644 index 2c6a8f028..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/high_res/smaller_forcing/tuned_galperin_forcing/part_2/giant_planet_shallow_water_deep_velocity_test_mk1_high_res.py +++ /dev/null @@ -1,175 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 32 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv_corrected', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) -diag.add_field('shallow_diagnostics', 'h_sqd_mean', time_avg=True) - -diag.add_field('shallow_diagnostics', 'e_kin_density', time_avg=True) -diag.add_field('shallow_diagnostics', 'eq_geopot', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_pot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_tot_real_units', time_avg=True) -diag.add_field('shallow_diagnostics', 'u_rms', time_avg=True) - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 600, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 1024, - 'num_lat' : 512, - 'num_fourier' : 341, - 'num_spherical' : 342, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for forcing_amplitude in [ 10.]: - - for damping_time in [ 10000., 1000., 100000.]: - u_deep_mag_val = 50. - - if u_deep_mag_val!=0.: - u_deep_merid_arr = [27] - else: - u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - ld_value = 0.025 - exp = Experiment('high_res_small_forcing_giant_planet_fixed_deep_ics_forced_'+str(damping_time)+'_rad_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid))+'_strong_forcing_'+str(forcing_amplitude), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * damping_time, #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13*forcing_amplitude, - 'n_total_forcing_max': 170, - 'n_total_forcing_min': 164, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES, multi_node=True) - for i in range(2,721): - exp.run(i, num_cores=NCORES, multi_node=True) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py deleted file mode 100644 index 0d5ce04b4..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk1.py +++ /dev/null @@ -1,167 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 16 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) - - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for ld_value in [10.0]: - for u_deep_mag_val in [50.,]: - #for u_deep_mag_val in [-50.]: - - if u_deep_mag_val!=0.: - u_deep_merid_arr = [3,] - else: - u_deep_merid_arr = [3] - - #u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - exp = Experiment('giant_planet_shallow_water_deep_velocity_test_mk2_no_forcing_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * 100., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':0., - 'n_total_forcing_max': 85, - 'n_total_forcing_min': 79, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,2101): - exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py b/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py deleted file mode 100644 index 711b2c34b..000000000 --- a/exp/shallow_water_giant_planet/first_param_sweep_deep_velocities/no_forcing_giant_planet_shallow_water_deep_velocity_test_mk2.py +++ /dev/null @@ -1,170 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 16 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -# diag.add_file('atmos_daily', 1, 'days', time_units='days') -# diag.add_file('atmos_timestep', 1200, 'seconds', time_units='days') - - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -diag.add_field('shallow_diagnostics', 'deep_geopot', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) -diag.add_field('shallow_diagnostics', 'e_kin', time_avg=True) - - - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for ld_value in [0.1, 10.0]: - # for u_deep_mag_val in [50., 0., 25.]: - for u_deep_mag_val in [50., 0.]: - - # if u_deep_mag_val!=0.: - # u_deep_merid_arr = [3, 11, 19, 26] - # else: - # u_deep_merid_arr = [3] - - u_deep_merid_arr = [3] - - for u_deep_merid in u_deep_merid_arr: - - exp = Experiment('giant_planet_shallow_water_deep_velocity_test_mk8_no_forcing_no_damping_ld_'+str(ld_value)+'_udeep_mag_'+str(u_deep_mag_val)+'_u_deep_merid_'+str(int(u_deep_merid)), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - number_ld_in_radius_units = ld_value - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential, - 'u_deep_mag' : u_deep_mag_val, - 'n_merid_deep_flow': u_deep_merid, - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : 0. - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':0., - 'n_total_forcing_max': 85, - 'n_total_forcing_min': 79, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,121): - exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py b/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py deleted file mode 100644 index 14c4b323c..000000000 --- a/exp/shallow_water_giant_planet/giant_planet_shallow_water_small_scale_stirring_test.py +++ /dev/null @@ -1,151 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 16 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) - - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for number_ld_in_radius_units in [0.1]: - - exp = Experiment('giant_shallow_stirring_test_experiment_uniform_smaller_120_scale_stirring_mk3_longer_damping_ld_'+str(number_ld_in_radius_units), codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * 10000., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13, - 'n_total_forcing_max': 125, - 'n_total_forcing_min': 119, - - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,121): - exp.run(i, num_cores=NCORES) diff --git a/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py b/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py deleted file mode 100644 index fa19bc3c5..000000000 --- a/exp/shallow_water_giant_planet/giant_planet_shallow_water_stirring_test.py +++ /dev/null @@ -1,148 +0,0 @@ -import os - -import numpy as np - -from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE - -import pdb - -NCORES = 8 -base_dir = os.path.dirname(os.path.realpath(__file__)) -# a CodeBase can be a directory on the computer, -# useful for iterative development -cb = ShallowCodeBase.from_directory(GFDL_BASE) - -# or it can point to a specific git repo and commit id. -# This method should ensure future, independent, reproducibility of results. -# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') - -# compilation depends on computer specific settings. The $GFDL_ENV -# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file -# is used to load the correct compilers. The env file is always loaded from -# $GFDL_BASE and not the checked out git repo. - -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - -# create an Experiment object to handle the configuration of model parameters -# and output diagnostics - -#Tell model how to write diagnostics -diag = DiagTable() -diag.add_file('atmos_monthly', 30, 'days', time_units='days') -diag.add_file('atmos_daily', 1, 'days', time_units='days') - -#Tell model which diagnostics to write -diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) -diag.add_field('shallow_diagnostics', 'vor', time_avg=True) -diag.add_field('shallow_diagnostics', 'div', time_avg=True) -diag.add_field('shallow_diagnostics', 'h', time_avg=True) -diag.add_field('shallow_diagnostics', 'pv', time_avg=True) -diag.add_field('shallow_diagnostics', 'stream', time_avg=True) -# diag.add_field('shallow_diagnostics', 'trs', time_avg=True) -# diag.add_field('shallow_diagnostics', 'tr', time_avg=True) -diag.add_field('stirring_mod', 'stirring', time_avg=True) -# diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) -diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) - - -#Empty the run directory ready to run - -#Define values for the 'core' namelist -namelist = Namelist({ - 'main_nml':{ - 'days' : 30, - 'hours' : 0, - 'minutes': 0, - 'seconds': 0, - 'dt_atmos': 1200, - 'calendar': 'no_calendar', - }, - - 'atmosphere_nml':{ - 'print_interval': 86400, - }, - -'fms_io_nml':{ - 'threading_write' :'single', - 'fileset_write': 'single' - }, - - 'fms_nml':{ - 'print_memory_usage':False, - 'domains_stack_size': 200000, - }, - - 'shallow_dynamics_nml':{ - 'num_lon' : 512, - 'num_lat' : 256, - 'num_fourier' : 170, - 'num_spherical' : 171, - 'fourier_inc' : 1, - 'damping_option' : 'resolution_dependent', - 'damping_order' : 4, - 'damping_coeff' : 1.e-04, - 'h_0' : 3.e04, - 'grid_tracer' : False, - 'spec_tracer' : False, - 'robert_coeff' : 0.04, - 'robert_coeff_tracer' : 0.04, - }, - - 'shallow_physics_nml':{ - 'fric_damp_time' : 0.0, - 'h_0' : 3.e04, #Scott and Polvani begin with L_D_polar = 10 radii in size, i.e. depth of 9,910 metres. - 'h_amp' : 0., - 'h_itcz' : 0., - }, - - 'stirring_nml': { - 'B':0.0, - 'do_localize': False, - }, - -}) - -#Lets do a run! -if __name__=="__main__": - - for number_ld_in_radius_units in [10.]: - - exp = Experiment('giant_shallow_stirring_test_experiment_uniform_stirring_mk3', codebase=cb) - - exp.diag_table = diag - exp.namelist = namelist - exp.clear_rundir() - - rotation_period = ((9.*3600.)+55.*60 + 30.) - omega = 2.*np.pi/ rotation_period - radius = 69911e3 - grav = 24.79 - - # Model uses geopotential as its height co-ordinate. So depth is h_0/grav. - - equilibrium_geopotential = (2.*number_ld_in_radius_units*omega*radius)**2. - equilibrium_depth = equilibrium_geopotential/grav - - exp.update_namelist({ - 'shallow_dynamics_nml':{ - 'h_0': equilibrium_geopotential - }, - 'shallow_physics_nml': { - 'h_0': equilibrium_geopotential, - 'therm_damp_time' : rotation_period * 1., #Thermal damping time in Scott and Polvani is 1 rotation period (v_l = 1) - }, - 'constants_nml': { - 'omega': omega, - 'radius': radius, - }, - 'stirring_nml': { - 'decay_time':10.*rotation_period, #Scott and Polvani use decorrelation time of 10 planetary rotations - i.e. a long time for Jupiter. - 'amplitude':3.e-13, - }, - - }) - - exp.run(1, use_restart=False, num_cores=NCORES) - for i in range(2,122): - exp.run(i, num_cores=NCORES) From e0992446335525b402c9b315d3bce6c539141f0a Mon Sep 17 00:00:00 2001 From: sit23 Date: Sat, 16 Oct 2021 21:11:13 +0100 Subject: [PATCH 25/34] Moving the cb.compile step inside the main executable part of the test cases. This prevents the code from compiling each time a different trip test is run, thus making the trip tests much faster. --- exp/test_cases/MiMA/MiMA_test_case.py | 5 +++-- exp/test_cases/axisymmetric/axisymmetric_test_case.py | 5 +++-- .../barotropic_vor_eq_stirring_test.py | 7 ++++--- .../barotropic_vor_eq_test.py | 5 +++-- exp/test_cases/bucket_hydrology/bucket_model_test_case.py | 4 ++-- exp/test_cases/frierson/frierson_test_case.py | 4 ++-- exp/test_cases/giant_planet/giant_planet_test_case.py | 4 ++-- exp/test_cases/held_suarez/held_suarez_test_case.py | 5 +++-- .../realistic_continents_fixed_sst_test_case.py | 4 ++-- .../realistic_continents_variable_qflux_test_case.py | 4 ++-- .../shallow_water/shallow_water_stirring_test.py | 4 ++-- exp/test_cases/shallow_water/shallow_water_test.py | 4 ++-- exp/test_cases/top_down_test/top_down_test.py | 3 +-- .../variable_co2_concentration/variable_co2_grey.py | 4 ++-- .../variable_co2_concentration/variable_co2_rrtm.py | 4 ++-- 15 files changed, 35 insertions(+), 31 deletions(-) diff --git a/exp/test_cases/MiMA/MiMA_test_case.py b/exp/test_cases/MiMA/MiMA_test_case.py index 270596744..586211935 100644 --- a/exp/test_cases/MiMA/MiMA_test_case.py +++ b/exp/test_cases/MiMA/MiMA_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('mima_test_experiment', codebase=cb) @@ -174,6 +172,9 @@ }) #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/axisymmetric/axisymmetric_test_case.py b/exp/test_cases/axisymmetric/axisymmetric_test_case.py index 52806f624..f9f879e42 100644 --- a/exp/test_cases/axisymmetric/axisymmetric_test_case.py +++ b/exp/test_cases/axisymmetric/axisymmetric_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('axisymmetric_test_case', codebase=cb) @@ -181,6 +179,9 @@ #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) \ No newline at end of file diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py index e03867687..ffe74e7c8 100644 --- a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('barotropic_stirring_test_experiment', codebase=cb) @@ -111,6 +109,9 @@ #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): - exp.run(i, num_cores=NCORES) + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py index 01cdcc687..11c7cce62 100644 --- a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('barotropic_test_experiment', codebase=cb) @@ -95,6 +93,9 @@ #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/bucket_hydrology/bucket_model_test_case.py b/exp/test_cases/bucket_hydrology/bucket_model_test_case.py index 73431552a..85f10bb17 100644 --- a/exp/test_cases/bucket_hydrology/bucket_model_test_case.py +++ b/exp/test_cases/bucket_hydrology/bucket_model_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('bucket_test_experiment', codebase=cb) @@ -179,6 +177,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) \ No newline at end of file diff --git a/exp/test_cases/frierson/frierson_test_case.py b/exp/test_cases/frierson/frierson_test_case.py index 6dfa83b10..6be2e58c0 100644 --- a/exp/test_cases/frierson/frierson_test_case.py +++ b/exp/test_cases/frierson/frierson_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('frierson_test_experiment', codebase=cb) @@ -173,6 +171,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/giant_planet/giant_planet_test_case.py b/exp/test_cases/giant_planet/giant_planet_test_case.py index db3e7d857..dfe625b81 100644 --- a/exp/test_cases/giant_planet/giant_planet_test_case.py +++ b/exp/test_cases/giant_planet/giant_planet_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('giant_planet_test_experiment', codebase=cb) @@ -207,6 +205,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/held_suarez/held_suarez_test_case.py b/exp/test_cases/held_suarez/held_suarez_test_case.py index 9ef5ca06a..fbab6baca 100644 --- a/exp/test_cases/held_suarez/held_suarez_test_case.py +++ b/exp/test_cases/held_suarez/held_suarez_test_case.py @@ -18,8 +18,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics @@ -104,6 +102,9 @@ #Lets do a run! if __name__ == '__main__': + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, num_cores=NCORES, use_restart=False) for i in range(2, 13): exp.run(i, num_cores=NCORES) # use the restart i-1 by default \ No newline at end of file diff --git a/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py b/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py index ef5b3d03e..23b039d2f 100644 --- a/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py +++ b/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py @@ -21,8 +21,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('realistic_continents_fixed_sst_test_experiment', codebase=cb) @@ -72,6 +70,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py b/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py index 522c9cf00..52d5e61d2 100644 --- a/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py +++ b/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py @@ -21,8 +21,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('realistic_continents_qflux_test_experiment', codebase=cb) @@ -72,6 +70,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/shallow_water/shallow_water_stirring_test.py b/exp/test_cases/shallow_water/shallow_water_stirring_test.py index d6571c2c1..2cf2d29dc 100644 --- a/exp/test_cases/shallow_water/shallow_water_stirring_test.py +++ b/exp/test_cases/shallow_water/shallow_water_stirring_test.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('shallow_stirring_test_experiment', codebase=cb) @@ -121,6 +119,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,122): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/shallow_water/shallow_water_test.py b/exp/test_cases/shallow_water/shallow_water_test.py index 53359d36c..7ba248a8f 100644 --- a/exp/test_cases/shallow_water/shallow_water_test.py +++ b/exp/test_cases/shallow_water/shallow_water_test.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('shallow_test_experiment', codebase=cb) @@ -103,6 +101,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/top_down_test/top_down_test.py b/exp/test_cases/top_down_test/top_down_test.py index 75854219a..2b97a71cf 100644 --- a/exp/test_cases/top_down_test/top_down_test.py +++ b/exp/test_cases/top_down_test/top_down_test.py @@ -18,8 +18,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create a diagnostics output file for daily snapshots diag = DiagTable() diag.add_file('atmos_daily', 1, 'days', time_units='days') @@ -97,6 +95,7 @@ }) if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase obls = [15] for obl in obls: diff --git a/exp/test_cases/variable_co2_concentration/variable_co2_grey.py b/exp/test_cases/variable_co2_concentration/variable_co2_grey.py index 4cdcab304..fb3b1eb0c 100644 --- a/exp/test_cases/variable_co2_concentration/variable_co2_grey.py +++ b/exp/test_cases/variable_co2_concentration/variable_co2_grey.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('variable_co2_grey_test_experiment', codebase=cb) @@ -172,6 +170,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) \ No newline at end of file diff --git a/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py b/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py index 9ba4173bc..228cb46a6 100644 --- a/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py +++ b/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('variable_co2_rrtm_test_experiment', codebase=cb) @@ -171,6 +169,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) From a9610f5cdd1176b69a4fb6b79df10862be8e418e Mon Sep 17 00:00:00 2001 From: sit23 Date: Sat, 16 Oct 2021 22:33:45 +0100 Subject: [PATCH 26/34] Adding shallow water and barotropic vorticity equation test cases to trip tests. Requires the tests to be more specific about which codebase each test case is using, and also requires a new simple diag table for the 2D cases. --- .../trip_test/trip_test_functions.py | 91 +++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/exp/test_cases/trip_test/trip_test_functions.py b/exp/test_cases/trip_test/trip_test_functions.py index 62fbf75ce..5d452b7d8 100644 --- a/exp/test_cases/trip_test/trip_test_functions.py +++ b/exp/test_cases/trip_test/trip_test_functions.py @@ -23,25 +23,29 @@ def get_nml_diag(test_case_name): sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/axisymmetric/')) from axisymmetric_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'bucket_model' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/bucket_hydrology/')) from bucket_model_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist - + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase + if 'frierson' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/frierson/')) from frierson_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'giant_planet' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/giant_planet/')) from giant_planet_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase #Make giant planet test case a lower resolution so that it runs in a finite time! nml_out['spectral_dynamics_nml']['num_fourier']=42 @@ -55,61 +59,87 @@ def get_nml_diag(test_case_name): from held_suarez_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'MiMA' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/MiMA/')) from MiMA_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'realistic_continents_fixed_sst' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/realistic_continents/')) from realistic_continents_fixed_sst_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'realistic_continents_variable_qflux' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/realistic_continents/')) from realistic_continents_variable_qflux_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'socrates_aquaplanet' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/socrates_test/')) from socrates_aquaplanet import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase if 'socrates_aquaplanet_cloud' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/socrates_test/')) from socrates_aquaplanet_cloud import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase if 'top_down_test' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/top_down_test/')) from top_down_test import namelist as nml_out input_files = [] + codebase_to_use = IscaCodeBase if 'variable_co2_grey' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/variable_co2_concentration/')) from variable_co2_grey import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'variable_co2_rrtm' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/variable_co2_concentration/')) from variable_co2_rrtm import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'ape_aquaplanet' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/ape_aquaplanet/')) from socrates_ape_aquaplanet_T42 import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase + + if 'barotropic_vort_eq_stirring' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/barotropic_vorticity_equation/')) + from barotropic_vor_eq_stirring_test import exp as exp_temp + from isca import BarotropicCodeBase + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=BarotropicCodeBase + + if 'shallow_water_stirring' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/shallow_water/')) + from shallow_water_stirring_test import exp as exp_temp + from isca import ShallowCodeBase + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=ShallowCodeBase - return nml_out, input_files + return nml_out, input_files, codebase_to_use def list_all_test_cases_implemented_in_trip_test(): @@ -127,7 +157,9 @@ def list_all_test_cases_implemented_in_trip_test(): 'top_down_test', 'variable_co2_grey', 'variable_co2_rrtm', - 'ape_aquaplanet'] + 'ape_aquaplanet', + 'barotropic_vort_eq_stirring', + 'shallow_water_stirring'] return exps_implemented @@ -149,6 +181,27 @@ def define_simple_diag_table(): return diag +def define_simple_diag_table_2d(shallow_or_baro): + """Defines a simple diag table for the + shallow water and barotropic vorticity test cases.""" + + if shallow_or_baro=='shallow': + diag_name = 'shallow_diagnostics' + elif shallow_or_baro=='barotropic': + diag_name = 'barotropic_diagnostics' + else: + raise NotImplementedError('incorrect option for 2d diag table') + + diag = DiagTable() + diag.add_file('atmos_daily', 1, 'days', time_units='days') + + #Tell model which diagnostics to write + diag.add_field(diag_name, 'ucomp', time_avg=True) + diag.add_field(diag_name, 'vcomp', time_avg=True) + diag.add_field(diag_name, 'vor', time_avg=True) + + return diag + def process_ids(base_commit_in, later_commit_in): @@ -172,8 +225,15 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r in the diag file. If there are any differences in the output variables then the test classed as a failure.""" data_dir_dict = {} - nml_use, input_files_use = get_nml_diag(test_case_name) - diag_use = define_simple_diag_table() + nml_use, input_files_use, codebase_obj = get_nml_diag(test_case_name) + + if 'shallow_water' in test_case_name: + diag_use = define_simple_diag_table_2d('shallow') + elif 'barotropic_vort_eq' in test_case_name: + diag_use = define_simple_diag_table_2d('barotropic') + else: + diag_use = define_simple_diag_table() + test_pass = True run_complete = True compile_successful=True @@ -181,10 +241,7 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r #Do the run for each of the commits in turn for s in [base_commit, later_commit]: exp_name = test_case_name+'_trip_test_21_'+s - if 'socrates' in test_case_name or 'ape_aquaplanet' in test_case_name: - cb = SocratesCodeBase(repo=repo_to_use, commit=s) - else: - cb = IscaCodeBase(repo=repo_to_use, commit=s) + cb = codebase_obj(repo=repo_to_use, commit=s) try: cb.compile() exp = Experiment(exp_name, codebase=cb) From d4c8256c138c240556a849c6e079b566f70ff871 Mon Sep 17 00:00:00 2001 From: sit23 Date: Tue, 26 Oct 2021 14:25:45 +0100 Subject: [PATCH 27/34] adding initial code to allow an initial condition for h, vor and div to be specified from an netcdf input file. As yet untested. Had to add extra fortran files to path-names as the interpolator function uses a lot of other modules. --- .../shallow_dynamics.F90 | 96 +++++++++++++------ src/extra/model/shallow/path_names | 10 ++ 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 7ba1ec3f8..17690d709 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -60,6 +60,8 @@ module shallow_dynamics_mod use stirring_mod, only : stirring, stirring_end +use interpolator_mod, only: interpolate_type,interpolator_init,CONSTANT,interpolator + !====================================================================================== implicit none private @@ -151,9 +153,16 @@ module shallow_dynamics_mod logical :: add_initial_vortex_pair = .false. logical :: add_initial_vortex_as_height = .true. +logical :: initial_condition_from_input_file=.false. +character(len=64) :: init_cond_file = 'init_cond_h_vor_div' +character(len=64) :: input_file_div_name = 'div' +character(len=64) :: input_file_height_name = 'height' +character(len=64) :: input_file_vor_name = 'vor' real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) +type(interpolate_type),save :: init_cond_interp + namelist /shallow_dynamics_nml/ check_fourier_imag, & south_to_north, triang_trunc, & num_lon, num_lat, num_fourier, & @@ -174,7 +183,12 @@ module shallow_dynamics_mod init_vortex_vor_f, & init_vortex_h_h_0, & add_initial_vortex_pair, & - add_initial_vortex_as_height + add_initial_vortex_as_height, & + initial_condition_from_input_file, & + init_cond_file, & + input_file_div_name, & + input_file_height_name, & + input_file_vor_name contains @@ -190,6 +204,7 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) integer :: i, j real, allocatable, dimension(:) :: glon_bnd, glat_bnd +real, allocatable, dimension(:,:) :: rad_lonb_2d, rad_latb_2d real :: xx, yy, dd, deep_geopot_global_mean, radius_loc_cyc, radius_loc_acyc integer :: ierr, io, unit, id_lon, id_lat, id_lonb, id_latb @@ -241,6 +256,16 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) allocate (glon_bnd (num_lon + 1)) allocate (glat_bnd (num_lat + 1)) call get_grid_boundaries (glon_bnd, glat_bnd, global=.true.) +allocate (rad_lonb_2d(is:ie+1, js:je+1)) +allocate (rad_latb_2d(is:ie+1, js:je+1)) + +do i = is,ie+1 + rad_lonb_2d(i,:) = glon_bnd(i) +enddo + +do j = js,je+1 + rad_latb_2d(:,j) = glat_bnd(j) +enddo coriolis = 2*omega*sin_lat @@ -279,6 +304,11 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) endif +if( initial_condition_from_input_file ) then + call interpolator_init( init_cond_interp, trim(init_cond_file)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) +endif + + do i = is, ie Dyn%grid%deep_geopot(i, js:je) = -2.*omega * u_deep_mag * radius * (1./(1.-n_merid_deep_flow**2.))*(-cos(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*cos(DEG_TO_RAD*deg_lat(js:je)) - n_merid_deep_flow * (sin(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*sin(DEG_TO_RAD*deg_lat(js:je))-sin(n_merid_deep_flow*(2.*atan(1.))))) enddo @@ -288,45 +318,53 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) if(Time == Time_init) then - Dyn%Grid%div(:,:,1) = 0.0 - Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) + if (initial_condition_from_input_file) then - do i = is, ie - Dyn%Grid%vor(i,js:je,1) = -((u_upper_mag_init * n_merid_deep_flow)/radius) * sin(DEG_to_RAD * deg_lat(js:je)) + call interpolator( init_cond_interp, Dyn%Grid%div(:,:,1), input_file_div_name ) + call interpolator( init_cond_interp, Dyn%Grid%h(:,:,1), input_file_height_name ) + call interpolator( init_cond_interp, Dyn%Grid%vor(:,:,1), input_file_vor_name ) - if (add_initial_vortex_pair) then + else + Dyn%Grid%div(:,:,1) = 0.0 + Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) - do j=js, je + do i = is, ie + Dyn%Grid%vor(i,js:je,1) = -((u_upper_mag_init * n_merid_deep_flow)/radius) * sin(DEG_to_RAD * deg_lat(js:je)) - radius_loc_cyc = ((min((deg_lon(i)-lon_centre_init_cyc)**2., (deg_lon(i)-lon_centre_init_cyc-360.)**2.)+(deg_lat(j)-lat_centre_init_cyc)**2.)**0.5)/init_vortex_radius_deg - radius_loc_acyc = ((min((deg_lon(i)-lon_centre_init_acyc)**2., (deg_lon(i)-lon_centre_init_acyc-360.)**2.)+(deg_lat(j)-lat_centre_init_acyc)**2.)**0.5)/init_vortex_radius_deg + if (add_initial_vortex_pair) then + do j=js, je - if(radius_loc_cyc.le.1.0 .and. radius_loc_acyc.le.1.0) then - call error_mesg('shallow_dynamics','Cannot initialise cyclone and anticyclone in same grid box ', FATAL) - endif + radius_loc_cyc = ((min((deg_lon(i)-lon_centre_init_cyc)**2., (deg_lon(i)-lon_centre_init_cyc-360.)**2.)+(deg_lat(j)-lat_centre_init_cyc)**2.)**0.5)/init_vortex_radius_deg + radius_loc_acyc = ((min((deg_lon(i)-lon_centre_init_acyc)**2., (deg_lon(i)-lon_centre_init_acyc-360.)**2.)+(deg_lat(j)-lat_centre_init_acyc)**2.)**0.5)/init_vortex_radius_deg - if(add_initial_vortex_as_height) then - if (radius_loc_cyc.le.2.0) then - Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * -h_0 * exp(-radius_loc_cyc**2.) - elseif (radius_loc_acyc.le.2.0) then - Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * h_0 * exp(-radius_loc_acyc**2.) - endif - else - if (radius_loc_cyc.le.1.0) then - Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * 2.*omega - elseif (radius_loc_acyc.le.1.0) then - Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * -2.*omega - endif - endif + if(radius_loc_cyc.le.1.0 .and. radius_loc_acyc.le.1.0) then + call error_mesg('shallow_dynamics','Cannot initialise cyclone and anticyclone in same grid box ', FATAL) + endif - enddo - + if(add_initial_vortex_as_height) then + if (radius_loc_cyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * -h_0 * exp(-radius_loc_cyc**2.) + elseif (radius_loc_acyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * h_0 * exp(-radius_loc_acyc**2.) + endif + else + if (radius_loc_cyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * 2.*omega + elseif (radius_loc_acyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * -2.*omega + endif - endif + endif - enddo + enddo + + + endif !add_initial_vortex_pair + enddo + + endif ! initial_condition_from_input_file call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) call trans_grid_to_spherical(Dyn%Grid%div(:,:,1), Dyn%Spec%div(:,:,1)) diff --git a/src/extra/model/shallow/path_names b/src/extra/model/shallow/path_names index 14f1c56f1..02dbdac8a 100644 --- a/src/extra/model/shallow/path_names +++ b/src/extra/model/shallow/path_names @@ -136,3 +136,13 @@ shared/platform/platform.F90 shared/time_manager/get_cal_time.F90 shared/time_manager/time_manager.F90 shared/tracer_manager/tracer_manager.F90 +atmos_shared/interpolator/interpolator.F90 +shared/horiz_interp/horiz_interp_bicubic.F90 +shared/horiz_interp/horiz_interp_bilinear.F90 +shared/horiz_interp/horiz_interp_conserve.F90 +shared/horiz_interp/horiz_interp.F90 +shared/horiz_interp/horiz_interp_spherical.F90 +shared/horiz_interp/horiz_interp_type.F90 +shared/time_interp/time_interp_external.F90 +shared/time_interp/time_interp.F90 +shared/axis_utils/axis_utils.F90 From 1a226ec79d8cc22c064a1118513215148d4a680b Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:40:35 +0100 Subject: [PATCH 28/34] Updated cftime calls to interface with latest version. --- src/extra/python/scripts/calendar_calc.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/extra/python/scripts/calendar_calc.py b/src/extra/python/scripts/calendar_calc.py index 2c40f6673..ac3aac37e 100644 --- a/src/extra/python/scripts/calendar_calc.py +++ b/src/extra/python/scripts/calendar_calc.py @@ -1,4 +1,4 @@ -from cftime import utime +import cftime from datetime import datetime from cmip_time import FakeDT import numpy as np @@ -8,9 +8,11 @@ def day_number_to_datetime_array(time_in, calendar_type, units_in): - cdftime = utime(units_in, calendar = calendar_type) + # cdftime = utime(units_in, calendar = calendar_type) - date_out = cdftime.num2date(time_in) + # date_out = cdftime.num2date(time_in) + + date_out = cftime.num2date(time_in, units_in, calendar=calendar_type) return date_out From 398a8df4ed3fe57856d99424388e23b8e0fcf6cb Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:41:05 +0100 Subject: [PATCH 29/34] Adding option to create timeseries file without time variation, and with multiple variables. --- src/extra/python/scripts/create_timeseries.py | 64 +++++++++++++------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/extra/python/scripts/create_timeseries.py b/src/extra/python/scripts/create_timeseries.py index 94a4a3998..b18d241d5 100644 --- a/src/extra/python/scripts/create_timeseries.py +++ b/src/extra/python/scripts/create_timeseries.py @@ -107,7 +107,11 @@ def create_time_arr(num_years,is_climatology,time_spacing): return time_arr,day_number,ntime,time_units,time_bounds -def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,variable_name,number_dict, time_bounds=None): +def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict, time_bounds=None): + """Default is to accept multiple data variables to put in the file. + Input format in data_dict = {variable_name:variable_array}. + Currently only accepts all 2D fields or all 3D fields. + Could be updated in future to accept a mix.""" output_file = Dataset(file_name, 'w', format='NETCDF3_CLASSIC') @@ -116,6 +120,10 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, else: is_thd=True + if time_arr is None: + add_time = False + else: + add_time = True lat = output_file.createDimension('lat', number_dict['nlat']) lon = output_file.createDimension('lon', number_dict['nlon']) @@ -137,7 +145,8 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfull = output_file.createDimension('pfull', number_dict['npfull']) phalf = output_file.createDimension('phalf', number_dict['nphalf']) - time = output_file.createDimension('time', 0) #s Key point is to have the length of the time axis 0, or 'unlimited'. This seems necessary to get the code to run properly. + if add_time: + time = output_file.createDimension('time', 0) #s Key point is to have the length of the time axis 0, or 'unlimited'. This seems necessary to get the code to run properly. latitudes = output_file.createVariable('lat','d',('lat',)) longitudes = output_file.createVariable('lon','d',('lon',)) @@ -146,7 +155,8 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfulls = output_file.createVariable('pfull','d',('pfull',)) phalfs = output_file.createVariable('phalf','d',('phalf',)) - times = output_file.createVariable('time','d',('time',)) + if add_time: + times = output_file.createVariable('time','d',('time',)) latitudes.units = 'degrees_N'.encode('utf-8') latitudes.cartesian_axis = 'Y' @@ -179,11 +189,11 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, phalfs.positive = 'down' phalfs.long_name = 'half pressure level' - - times.units = time_units - times.calendar = 'THIRTY_DAY_MONTHS' - times.calendar_type = 'THIRTY_DAY_MONTHS' - times.cartesian_axis = 'T' + if add_time: + times.units = time_units + times.calendar = 'THIRTY_DAY_MONTHS' + times.calendar_type = 'THIRTY_DAY_MONTHS' + times.cartesian_axis = 'T' if time_bounds is not None: @@ -198,12 +208,6 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, times.bounds = 'time_bounds' - - if is_thd: - output_array_netcdf = output_file.createVariable(variable_name,'f4',('time','pfull', 'lat','lon',)) - else: - output_array_netcdf = output_file.createVariable(variable_name,'f4',('time','lat','lon',)) - latitudes[:] = lats longitudes[:] = lons @@ -216,14 +220,34 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfulls[:] = p_full phalfs[:] = p_half - if type(time_arr[0])!=np.float64 and type(time_arr[0])!=np.int64 : - times[:] = date2num(time_arr,units='days since 0001-01-01 00:00:00.0',calendar='360_day') - else: - times[:] = time_arr - - output_array_netcdf[:] = data + if add_time: + if type(time_arr[0])!=np.float64 and type(time_arr[0])!=np.int64 : + times[:] = date2num(time_arr,units='days since 0001-01-01 00:00:00.0',calendar='360_day') + else: + times[:] = time_arr + + for variable_name in data_dict.keys(): + if is_thd: + if add_time: + three_d_dims = ('time','pfull', 'lat','lon',) + else: + three_d_dims = ('pfull', 'lat','lon',) + + output_array_netcdf = output_file.createVariable(variable_name,'f4',three_d_dims) + else: + if add_time: + two_d_dims = ('time','lat','lon',) + else: + two_d_dims = ('lat','lon',) + output_array_netcdf = output_file.createVariable(variable_name,'f4',two_d_dims) + + output_array_netcdf[:] = data_dict[variable_name] output_file.close() +def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,variable_name,number_dict, time_bounds=None): + """Special interface for script wanting to output 1 variable only.""" + data_dict_to_send = {variable_name:data} + output_multiple_variables_to_file(data_dict_to_send,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict,time_bounds=time_bounds) From 9044fbb2f12e668b45d35cba180180d73c087c3d Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:42:05 +0100 Subject: [PATCH 30/34] When defining the h field in the input file, take the convention that the h term passed in is the perturbation to the mean, and so should have an area mean of zero. This means that the h-0 parameter in the code is what sets the mean depth. --- src/atmos_spectral_shallow/shallow_dynamics.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 index 17690d709..14b7e89db 100644 --- a/src/atmos_spectral_shallow/shallow_dynamics.F90 +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -324,6 +324,8 @@ subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) call interpolator( init_cond_interp, Dyn%Grid%h(:,:,1), input_file_height_name ) call interpolator( init_cond_interp, Dyn%Grid%vor(:,:,1), input_file_vor_name ) + Dyn%Grid%h(:,:,1) = Dyn%Grid%h(:,:,1)+h_0 !want to make sure that we keep h_0 consistent, so make sure mean of h input is zero, and add h_0 on afterwards... + else Dyn%Grid%div(:,:,1) = 0.0 Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) From d4b15294a54fb8deef92f10ae4a3c3ad4adc4d8c Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:42:39 +0100 Subject: [PATCH 31/34] Add script to create sw initial conditions, initially following the Rostami et al 2017 Saturn hexagon paper. --- .../scripts/shallow_water_init_conds.py | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/extra/python/scripts/shallow_water_init_conds.py diff --git a/src/extra/python/scripts/shallow_water_init_conds.py b/src/extra/python/scripts/shallow_water_init_conds.py new file mode 100644 index 000000000..b37fc4ca0 --- /dev/null +++ b/src/extra/python/scripts/shallow_water_init_conds.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*-s +from typing import NoReturn +import numpy as np +import pdb +import create_timeseries as cts +import xarray as xar +import gauss_grid as gg +import matplotlib.pyplot as plt +import windspharm as wsp +import pdb + +def convert_to_vor_div(u_in, v_in, lat_arr): + """convert spherical polar velocities to vor and div""" + + + uwnd, uwnd_info = wsp.tools.prep_data(u_in, 'yx') + vwnd, vwnd_info = wsp.tools.prep_data(v_in, 'yx') + + # It is also required that the latitude dimension is north-to-south. Again the + # bundled tools make this easy. + lat_1d_ordered, uwnd, vwnd = wsp.tools.order_latdim(lat_arr[:,0], uwnd, vwnd) + + # Create a VectorWind instance to handle the computation of streamfunction and + # velocity potential. + w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=55000e3) + + # Compute the streamfunction and velocity potential. Also use the bundled + # tools to re-shape the outputs to the 4D shape of the wind components as they + # were read off files. + vor = w.vorticity() + div = w.divergence() + # sf, vp = w.sfvp() + vor = wsp.tools.recover_data(vor, uwnd_info) + div = wsp.tools.recover_data(div, uwnd_info) + + return vor[::-1,:], div[::-1,:] #need to reverse latitude reordering + +def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, m, r_0, planet_radius, northern_hemisphere=True): + """Configure an initial condition for u, v and h given some + balance condition""" + + deformation_scale = 3200e3 #p62 of Rostami et al 2017 + f_0 = 3.2e-4 + timescale = (f_0)**-1 + velocity_scale = deformation_scale/timescale + + lat_rad_2d = np.deg2rad(lat_in) + lon_rad_2d = np.deg2rad(lon_in) + + if northern_hemisphere: + r_array = (planet_radius * (np.pi/2. - lat_rad_2d))/deformation_scale #non-dim + else: + r_array = (planet_radius * (np.pi/2. + lat_rad_2d))/deformation_scale #non-dim + + v = np.zeros_like(lat_in) + u = epsilon * ((r_array - r_0)**alpha)* np.exp(-m*((r_array-r_0)**beta)) + + v_si_units = v * velocity_scale + u_si_units = u * velocity_scale + + if northern_hemisphere: + grad_geopot = ((u_si_units**2)/(r_array* deformation_scale)) + (f_0*np.sin(lat_rad_2d)*u_si_units) + else: + #I've changed the sign of the coriolis term here. Clearly this isn't really happening, but in this funny radial coordinate system the sign of u would change for the opposite hemisphere, thus necessitating the sign change. + grad_geopot = ((u_si_units**2)/(r_array* deformation_scale)) - (f_0*np.sin(lat_rad_2d)*u_si_units) + + geopotential = np.zeros_like(grad_geopot) + + if northern_hemisphere: + for lat_idx in range(1, len(lat_rad_2d[:,0])): + geopotential[lat_idx,:] = geopotential[lat_idx-1,:] + 0.5*(grad_geopot[lat_idx-1,:]+grad_geopot[lat_idx,:])*(r_array[lat_idx]-r_array[lat_idx-1]) + else: + r_array_opposite = r_array[::-1,:] + grad_geopot_opposite = grad_geopot[::-1,:] + for lat_idx in range(1, len(lat_rad_2d[:,0])): + geopotential[lat_idx,:] = geopotential[lat_idx-1,:] + 0.5*(grad_geopot_opposite[lat_idx-1,:]+grad_geopot_opposite[lat_idx,:])*(r_array_opposite[lat_idx]-r_array_opposite[lat_idx-1]) + + geopotential = geopotential[::-1,:] + + #we want to pass a geopotential field that has an area-mean of zero. This is because we want to preserve the mean geopotential that the model sets as its h_0 parameter. + + delta_lat_arr = np.diff(latb_in, axis=0)[:,0:-1] + + area_array = np.cos(np.deg2rad(lat_in))*np.deg2rad(delta_lat_arr) + + area_av_geopot = np.sum(geopotential*area_array)/np.sum(area_array) + + geopotential_av_removed = geopotential-area_av_geopot + + area_av_final = np.sum(geopotential_av_removed*area_array)/np.sum(area_array) + + print(f'old mean = {area_av_geopot}, final area_av geopot = {area_av_final}') + + geopotential_si_units = geopotential_av_removed * deformation_scale + + h_0 = (deformation_scale*f_0)**2. + + return u_si_units, v_si_units, geopotential_si_units, h_0, grad_geopot + +nlat=128 +nlon=256 + +latitudes, latitude_bounds_2 = gg.gaussian_latitudes(int(nlat/2)) +latitude_bounds = [latitude_bound[0] for latitude_bound in latitude_bounds_2] + [latitude_bounds_2[-1][1]] + +longitudes = np.linspace(0., 360., nlon, endpoint=False) +delta_lon = longitudes[1]-longitudes[0] +longitude_bounds = [lon_val-(0.5*delta_lon) for lon_val in longitudes] + [np.max(longitudes)+(0.5*delta_lon)] +time_arr_adj=None + +lon_array_2d, lat_array_2d = np.meshgrid(longitudes, latitudes) +lonb_array_2d, latb_array_2d = np.meshgrid(longitude_bounds, latitude_bounds) + + +epsilon = 0.15*2. +alpha = 0.42 +beta = 1.3 +r_0 = 0. +m_param = 1. +planet_radius = 55000e3 + +u_array_vortex, v_array_vortex, height_array_vortex, h_0, grad_geopot_vortex = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius) + +u_array_vortex_sp, v_array_vortex_sp, height_array_vortex_sp, h_0_sp, grad_geopot_vortex_sp = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius, northern_hemisphere=False) + +epsilon = 0.08 +alpha = 0. +beta = 2. +r_0 = 3.37 +m_param = 3. +planet_radius = 55000e3 + +u_array_jet, v_array_jet, height_array_jet, h_0, grad_geopot_jet = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius) + +u_array_jet_sp, v_array_jet_sp, height_array_jet_sp, h_0_sp, grad_geopot_jet_sp = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius, northern_hemisphere=False) + + +u_array_total = u_array_vortex+u_array_vortex_sp + u_array_jet+u_array_jet_sp +v_array_total = v_array_vortex+v_array_vortex_sp + v_array_jet+v_array_jet_sp +height_array_total = height_array_vortex+height_array_vortex_sp + height_array_jet+height_array_jet_sp +grad_geopot_total = grad_geopot_vortex + grad_geopot_vortex_sp + grad_geopot_jet + grad_geopot_jet_sp + +vor_array, div_array = convert_to_vor_div(u_array_total, v_array_total, height_array_total) + + +p_full=None +p_half=None + +npfull=None +nphalf=None + +#Output it to a netcdf file. + +file_name='rostami_t85_jet_and_vortex_mk7.nc' + + +number_dict={} +number_dict['nlat']=nlat +number_dict['nlon']=nlon +number_dict['nlatb']=nlat+1 +number_dict['nlonb']=nlon+1 +number_dict['npfull']=npfull +number_dict['nphalf']=nphalf +number_dict['ntime']=None + +data_dict = { + 'vor': vor_array, + 'height': height_array_total, + 'div': div_array, + 'ucomp': u_array_total, + 'vcomp': v_array_total, + 'grad_geopot': grad_geopot_total +} + + +time_units=None + +cts.output_multiple_variables_to_file(data_dict,latitudes,longitudes,latitude_bounds,longitude_bounds,p_full,p_half,time_arr_adj,time_units,file_name,number_dict) + +print(f'Must set h_0 parameter in code to be {h_0}') + + + From deb21817cd616181cf649319ae0d6809c64e4d94 Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:43:46 +0100 Subject: [PATCH 32/34] Adding test case for shallow water with prescribed initial conditions, following the rostami et al 2017 paper. --- .../shallow_water/prescribed_ics_test.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 exp/test_cases/shallow_water/prescribed_ics_test.py diff --git a/exp/test_cases/shallow_water/prescribed_ics_test.py b/exp/test_cases/shallow_water/prescribed_ics_test.py new file mode 100644 index 000000000..52a50f95a --- /dev/null +++ b/exp/test_cases/shallow_water/prescribed_ics_test.py @@ -0,0 +1,109 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +input_files = [os.path.join(base_dir,'input/rostami_t85_jet_and_vortex_mk7.nc')] + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_test_experiment_nc_init_cond_rostami_1_daily_t85_mk7', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_hourly', 1, 'hours', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) + + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 1, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 1048576.0, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + 'initial_condition_from_input_file':True, + 'init_cond_file':'rostami_t85_jet_and_vortex_mk7' + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'therm_damp_time' : 0.0, + }, + + 'constants_nml':{ + 'radius':55000e3, + 'omega': 1.6e-4 + } +}) + +#Lets do a run! +if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.inputfiles = input_files + exp.run(1, use_restart=False, num_cores=NCORES) + # for i in range(2,121): + # exp.run(i, num_cores=NCORES) From 23cc2b68ab2f711d9ac12094da6963d5d31d1c3b Mon Sep 17 00:00:00 2001 From: sit23 Date: Wed, 27 Oct 2021 14:46:53 +0100 Subject: [PATCH 33/34] Adding references and general planet radius to init cond script. --- src/extra/python/scripts/shallow_water_init_conds.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/extra/python/scripts/shallow_water_init_conds.py b/src/extra/python/scripts/shallow_water_init_conds.py index b37fc4ca0..7447fae3c 100644 --- a/src/extra/python/scripts/shallow_water_init_conds.py +++ b/src/extra/python/scripts/shallow_water_init_conds.py @@ -9,10 +9,9 @@ import windspharm as wsp import pdb -def convert_to_vor_div(u_in, v_in, lat_arr): +def convert_to_vor_div(u_in, v_in, lat_arr, planet_radius): """convert spherical polar velocities to vor and div""" - uwnd, uwnd_info = wsp.tools.prep_data(u_in, 'yx') vwnd, vwnd_info = wsp.tools.prep_data(v_in, 'yx') @@ -22,7 +21,7 @@ def convert_to_vor_div(u_in, v_in, lat_arr): # Create a VectorWind instance to handle the computation of streamfunction and # velocity potential. - w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=55000e3) + w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=planet_radius) # Compute the streamfunction and velocity potential. Also use the bundled # tools to re-shape the outputs to the 4D shape of the wind components as they @@ -37,7 +36,8 @@ def convert_to_vor_div(u_in, v_in, lat_arr): def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, m, r_0, planet_radius, northern_hemisphere=True): """Configure an initial condition for u, v and h given some - balance condition""" + balance condition. Use parameters and gradient-wind balance for Saturn + from 10.1016/j.icarus.2017.06.006""" deformation_scale = 3200e3 #p62 of Rostami et al 2017 f_0 = 3.2e-4 @@ -111,6 +111,7 @@ def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, lon_array_2d, lat_array_2d = np.meshgrid(longitudes, latitudes) lonb_array_2d, latb_array_2d = np.meshgrid(longitude_bounds, latitude_bounds) +#Note that in the following we're making the initial condition symmetric about the equator. This is because if you only set the initial conditions in the northern hemisphere then you end up needing a very large set of latitudinal functions to get that level of asymmetry, and the code gets very upset when translating that to a finite spectral representation. Making it symmetric gets rid of this problem, at least to some extent. epsilon = 0.15*2. alpha = 0.42 @@ -140,7 +141,7 @@ def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, height_array_total = height_array_vortex+height_array_vortex_sp + height_array_jet+height_array_jet_sp grad_geopot_total = grad_geopot_vortex + grad_geopot_vortex_sp + grad_geopot_jet + grad_geopot_jet_sp -vor_array, div_array = convert_to_vor_div(u_array_total, v_array_total, height_array_total) +vor_array, div_array = convert_to_vor_div(u_array_total, v_array_total, height_array_total, planet_radius) p_full=None From 72c72199c0396cebb5636ae2d56babc3a79bc5a0 Mon Sep 17 00:00:00 2001 From: sit23 Date: Thu, 28 Oct 2021 13:03:16 +0100 Subject: [PATCH 34/34] Updated windspharm call to note that this is latitude on a gaussian grid. --- src/extra/python/scripts/shallow_water_init_conds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extra/python/scripts/shallow_water_init_conds.py b/src/extra/python/scripts/shallow_water_init_conds.py index 7447fae3c..2f8bca068 100644 --- a/src/extra/python/scripts/shallow_water_init_conds.py +++ b/src/extra/python/scripts/shallow_water_init_conds.py @@ -21,7 +21,7 @@ def convert_to_vor_div(u_in, v_in, lat_arr, planet_radius): # Create a VectorWind instance to handle the computation of streamfunction and # velocity potential. - w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=planet_radius) + w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=planet_radius, gridtype='gaussian') # Compute the streamfunction and velocity potential. Also use the bundled # tools to re-shape the outputs to the 4D shape of the wind components as they @@ -152,7 +152,7 @@ def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, #Output it to a netcdf file. -file_name='rostami_t85_jet_and_vortex_mk7.nc' +file_name='rostami_t85_jet_and_vortex_mk7_gg.nc' number_dict={}