Skip to content

Commit

Permalink
Remove goto statements from chgres_cube (#775)
Browse files Browse the repository at this point in the history
The WAM option was using the NRLMSISE-00 library, which contains numerous 'goto'
statements. That library was replaced with the latest NRLMSIS 2.1 library, which 
does not use 'goto' statements and is also a welcome improvement in initializing 
the upper atmosphere. 

The new library included a unit test, which was added to run under Github
actions.

Fixes #759.
  • Loading branch information
akubaryk committed Mar 23, 2023
1 parent 9432bda commit 4494bd2
Show file tree
Hide file tree
Showing 22 changed files with 3,466 additions and 2,808 deletions.
Binary file added parm/msis_lib/msis21.parm
Binary file not shown.
1 change: 1 addition & 0 deletions reg_tests/chgres_cube/c96.fv3.netcdf2wam.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export VCOORD_FILE=${HOMEufs}/fix/am/global_hyblev.l64.txt
export INPUT_TYPE="gaussian_netcdf"
export CONVERT_SFC=".false."
export CONVERT_NST=".false."
export WAM_PARM_FILE=${HOMEufs}/parm/msis_lib/msis21.parm

export CDATE=2020020200

Expand Down
5 changes: 4 additions & 1 deletion sorc/chgres_cube.fd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ set(lib_src

set(exe_src chgres.F90)

add_subdirectory(msis2.1.fd)

if(CMAKE_Fortran_COMPILER_ID MATCHES "^(Intel)$")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -r8 -assume byterecl")
elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^(GNU)$")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-0 -fdefault-real-8")

# Turn on this argument mismatch flag for gfortran10.
if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 10)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fallow-argument-mismatch")
Expand Down Expand Up @@ -55,6 +57,7 @@ target_link_libraries(
sp::sp_d
w3nco::w3nco_d
esmf
msis2
MPI::MPI_Fortran
NetCDF::NetCDF_Fortran)

Expand Down
20 changes: 9 additions & 11 deletions sorc/chgres_cube.fd/atmosphere.F90
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module atmosphere
terrain_target_grid

use program_setup, only : vcoord_file_target_grid, &
wam_cold_start, &
wam_cold_start, wam_parm_file, &
cycle_year, cycle_mon, &
cycle_day, cycle_hour, &
regional, &
Expand Down Expand Up @@ -349,7 +349,7 @@ subroutine atmosphere_driver(localpet)
call vintg

if( wam_cold_start ) then
call vintg_wam (cycle_year,cycle_mon,cycle_day,cycle_hour)
call vintg_wam (cycle_year,cycle_mon,cycle_day,cycle_hour,wam_parm_file)
endif

!-----------------------------------------------------------------------------------
Expand Down Expand Up @@ -1521,15 +1521,17 @@ END SUBROUTINE VINTG_THOMP_MP_CLIMO
!! @param [in] month initial month
!! @param [in] day initial day
!! @param [in] hour initial hour
!! @param [in] pf path to MSIS2.1 parm file
!!
!! @author Hann-Ming Henry Juang NCEP/EMC
SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR)
SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR,PF)

IMPLICIT NONE

include 'mpif.h'

INTEGER, INTENT(IN) :: YEAR,MONTH,DAY,HOUR
CHARACTER(*), INTENT(IN) :: PF

REAL(ESMF_KIND_R8), PARAMETER :: AMO = 15.9994 ! molecular weight of o
REAL(ESMF_KIND_R8), PARAMETER :: AMO2 = 31.999 !molecular weight of o2
Expand Down Expand Up @@ -1650,11 +1652,9 @@ SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR)
DO K=1,LEV_TARGET
IF(P2PTR(I,J,K).le.P1PTR(I,J,LEV_INPUT)) THEN
KREF =K-1
!x print*,'VINTG_WAM: KREF P1 P2 ',KREF,P1PTR(I,J,LEV_INPUT),P2PTR(I,J,K)
GO TO 11
EXIT
ENDIF
ENDDO
11 CONTINUE
!
DO K=KREF,LEV_TARGET
COE = P2PTR(I,J,K) / P2PTR(I,J,KREF)
Expand Down Expand Up @@ -1683,10 +1683,9 @@ SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR)
DO K=1,LEV_TARGET
IF(P2PTR(I,J,K).le.P1PTR(I,J,LEV_INPUT)) THEN
KREF =K-1
GO TO 22
EXIT
ENDIF
ENDDO
22 CONTINUE
!
DO K=KREF,LEV_TARGET
COE = MIN(1.0, P2PTR(I,J,K) / P2PTR(I,J,KREF) )
Expand All @@ -1712,7 +1711,7 @@ SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR)
DO K=1,LEV_TARGET
PRMB(K) = P2PTR(I,J,K) * 0.01
ENDDO
CALL GETTEMP(ICDAY,1,DEGLAT,1,PRMB,LEV_TARGET,TEMP,ON,O2N,N2N)
CALL GETTEMP(ICDAY,DEGLAT,PRMB,LEV_TARGET,PF,TEMP,ON,O2N,N2N)
!
DO K=1,LEV_TARGET
SUMMASS = ON(K)*AMO+O2N(K)*AMO2+N2N(K)*AMN2
Expand All @@ -1730,10 +1729,9 @@ SUBROUTINE VINTG_WAM (YEAR,MONTH,DAY,HOUR)
DO K=1,LEV_TARGET
IF(P2PTR(I,J,K).le.P1PTR(I,J,LEV_INPUT)) THEN
KREF =K-1
GO TO 33
EXIT
ENDIF
ENDDO
33 CONTINUE
!
DO K=KREF,LEV_TARGET
T2PTR(I,J,K) = TEMP(K)
Expand Down
4 changes: 2 additions & 2 deletions sorc/chgres_cube.fd/docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ FILE_PATTERNS = *.F90 \
# be searched for input files as well.
# The default value is: NO.

RECURSIVE = YES
RECURSIVE = NO

# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
Expand All @@ -906,7 +906,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.

EXCLUDE = ../../fre-nctools.fd
EXCLUDE =

# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
Expand Down
26 changes: 26 additions & 0 deletions sorc/chgres_cube.fd/msis2.1.fd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
include(FetchContent)
include(CheckFortranCompilerFlag)

add_library(msis2
msis_utils.F90
msis_constants.F90
msis_init.F90
msis_gfn.F90
msis_tfn.F90
msis_dfn.F90
msis_calc.F90
msis_gtd8d.F90)

set_target_properties(msis2 PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include)

target_include_directories(msis2 INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include)

if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
# msis_calc:bspline has argument mismatch on nodes variable
target_compile_options(msis2 PRIVATE -std=legacy)
endif()

target_link_libraries(
msis2
PUBLIC
MPI::MPI_Fortran)
223 changes: 223 additions & 0 deletions sorc/chgres_cube.fd/msis2.1.fd/msis_calc.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
!#######################################################################
! MSIS® (NRL-SOF-014-1) SOFTWARE
! NRLMSIS® empirical atmospheric model software. Use is governed by the
! Open Source Academic Research License Agreement contained in the file
! nrlmsis2.1_license.txt, which is part of this software package. BY
! USING OR MODIFYING THIS SOFTWARE, YOU ARE AGREEING TO THE TERMS AND
! CONDITIONS OF THE LICENSE.
!#######################################################################

!!! ===========================================================================
!!! NRLMSIS 2.1:
!!! Neutral atmosphere empirical model from the surface to lower exosphere
!!! ===========================================================================
!!!
!!! MSISCALC: Interface with re-ordered input arguments and output arrays.
!
! PREREQUISITES:
! Must first run MSISINIT to load parameters and set switches. The
! MSISCALC subroutine checks for initialization and does a default
! initialization if necessary. This self-initialization will be removed
! in future versions.
!
! CALLING SEQUENCE:
! CALL MSISCALC(DAY, UTSEC, Z, LAT, LON, SFLUXAVG, SFLUX, AP, TN, DN, [TEX])
!
! INPUT VARIABLES:
! DAY Day of year (1.0 to 365.0 or 366.0)
! UTSEC Universal time (seconds)
! Z Geodetic altitude (km) (default) or Geopotential height (km)
! LAT Geodetic latitude (deg)
! LON Geodetic longitude (deg)
! SFLUXAVG 81 day average, centered on input time, of F10.7 solar
! activity index
! SFLUX Daily F10.7 for previous day
! AP Geomagnetic activity index array:
! (1) Daily Ap
! (2) 3 hr ap index for current time
! (3) 3 hr ap index for 3 hrs before current time
! (4) 3 hr ap index for 6 hrs before current time
! (5) 3 hr ap index for 9 hrs before current time
! (6) Average of eight 3 hr ap indices from 12 to 33 hrs
! prior to current time
! (7) Average of eight 3 hr ap indices from 36 to 57 hrs
! prior to current time
! AP(2:7) are only used when switch_legacy(9) = -1.0 in MSISINIT
!
! NOTES ON INPUT VARIABLES:
! - The day-of-year dependence of the model only uses the DAY argument. If
! a continuous day-of-year dependence is desired, this argument should
! include the fractional day (e.g., DAY = <day of year> + UTSEC/86400.0
! - If lzalt_type = .true. (default) in the MSISINIT call, then Z is
! treated as geodetic altitude.
! If lzalt_type = .false., then Z is treated as geopotential height.
! - F107 and F107A values are the 10.7 cm radio flux at the Sun-Earth
! distance, not the radio flux at 1 AU.
!
! OUTPUT VARIABLES:
! TN Temperature at altitude (K)
! DN(1) Total mass density (kg/m3)
! DN(2) N2 number density (m-3)
! DN(3) O2 number density (m-3)
! DN(4) O number density (m-3)
! DN(5) He number density (m-3)
! DN(6) H number density (m-3)
! DN(7) Ar number density (m-3)
! DN(8) N number density (m-3)
! DN(9) Anomalous oxygen number density (m-3)
! DN(10) NO number density (m-3)
! TEX Exospheric temperature (K) (optional argument)
!
! NOTES ON OUTPUT VARIABLES:
! - Missing density values are returned as 9.999e-38
! - Species included in mass density calculation are set in MSISINIT
!
!!! =========================================================================

!**************************************************************************************************
! MSIS_CALC Module: Contains main MSIS entry point
!**************************************************************************************************
module msis_calc

contains

!==================================================================================================
! MSISCALC: The main MSIS subroutine entry point
!==================================================================================================
subroutine msiscalc(day,utsec,z,lat,lon,sfluxavg,sflux,ap,tn,dn,tex)

use msis_constants, only : rp, dmissing, lnp0, Mbarg0divkB, kB, nspec, nodesTN, nd, zetaF, zetaB, &
Hgamma, zetagamma, maxnbf
use msis_init, only : msisinit, initflag, zaltflag, specflag, massflag, masswgt, etaTN
use msis_gfn, only : globe
use msis_tfn, only : tnparm, tfnparm, tfnx
use msis_dfn, only : dnparm, dfnparm, dfnx
use msis_utils, only : alt2gph, bspline, dilog

implicit none

real(kind=rp), intent(in) :: day
real(kind=rp), intent(in) :: utsec
real(kind=rp), intent(in) :: z
real(kind=rp), intent(in) :: lat
real(kind=rp), intent(in) :: lon
real(kind=rp), intent(in) :: sfluxavg,sflux,ap(1:7)
real(kind=rp), intent(out) :: tn, dn(1:10)
real(kind=rp), intent(out), optional :: tex

real(kind=rp), save :: lastday = -9999.0
real(kind=rp), save :: lastutsec = -9999.0
real(kind=rp), save :: lastlat = -9999.0
real(kind=rp), save :: lastlon = -9999.0
real(kind=rp), save :: lastz = -9999.0
real(kind=rp), save :: lastsflux = -9999.0
real(kind=rp), save :: lastsfluxavg = -9999.0
real(kind=rp), save :: lastap(1:7) = -9999.0
real(kind=rp), save :: gf(0:maxnbf-1)
real(kind=rp), save :: Sz(-5:0,2:6)
integer, save :: iz
type(tnparm), save :: tpro
type(dnparm), save :: dpro(1:nspec-1)

real(8) :: zaltd, latd
real(kind=rp) :: zeta, lndtotz, Vz, Wz, HRfact, lnPz, delz
integer :: i, j, kmax, ispec

! Check if model has been initialized; if not, perform default initialization
if (.not. initflag) call msisinit()

! Calculate geopotential height, if necessary
if(zaltflag) then
zaltd = dble(z)
latd = dble(lat)
zeta = alt2gph(latd,zaltd)
else
zeta = z
endif

! If only altitude changes then update the local spline weights
if (zeta .lt. zetaB) then
if (zeta .ne. lastz) then
if (zeta .lt. zetaF) then
kmax = 5
else
kmax = 6
endif
call bspline(zeta,nodesTN,nd+2,kmax,etaTN,Sz,iz)
lastz = zeta
endif
endif

! If location, time, or solar/geomagnetic conditions change then recompute the profile parameters
if ((day .ne. lastday) .or. (utsec .ne. lastutsec) .or. &
(lat .ne. lastlat) .or. (lon .ne. lastlon) .or. &
(sflux .ne. lastsflux) .or. (sfluxavg .ne. lastsfluxavg) .or. &
any(ap .ne. lastap)) then
call globe(day,utsec,lat,lon,sfluxavg,sflux,ap,gf)
call tfnparm(gf,tpro)
do ispec = 2, nspec-1
if (specflag(ispec)) call dfnparm(ispec,gf,tpro,dpro(ispec))
enddo
lastday = day
lastutsec = utsec
lastlat = lat
lastlon = lon
lastsflux = sflux
lastsfluxavg = sfluxavg
lastap = ap
endif

! Exospheric temperature
if (present(tex)) then
tex = tpro%tex
endif

! Temperature at altitude
tn = tfnx(zeta,iz,Sz(-3:0,4),tpro)

! Temperature integration terms at altitude, total number density
delz = zeta - zetaB
if (zeta .lt. zetaF) then
i = max(iz-4,0)
if (iz .lt. 4) then
j = -iz
else
j = -4
endif
Vz = dot_product(tpro%beta(i:iz),Sz(j:0,5)) + tpro%cVS
Wz = 0.0_rp
lnPz = lnP0 - Mbarg0divkB*(Vz - tpro%Vzeta0)
lndtotz = lnPz - log(kB*tn)
else
if (zeta .lt. zetaB) then
Vz = dot_product(tpro%beta(iz-4:iz),Sz(-4:0,5)) + tpro%cVS
Wz = dot_product(tpro%gamma(iz-5:iz),Sz(-5:0,6)) + tpro%cVS*delz + tpro%cWS
else
Vz = (delz + log(tn/tpro%tex)/tpro%sigma)/tpro%tex + tpro%cVB
Wz = (0.5_rp*delz*delz + dilog(tpro%b*exp(-tpro%sigma*delz))/tpro%sigmasq)/tpro%tex &
+ tpro%cVB*delz + tpro%cWB
endif
endif

! Species number densities at altitude
HRfact = 0.5_rp * (1.0_rp + tanh(Hgamma*(zeta - zetagamma))) !Reduction factor for chemical/dynamical correction scale height below zetagamma
do ispec = 2, nspec-1
if (specflag(ispec)) then
dn(ispec) = dfnx(zeta,tn,lndtotz,Vz,Wz,HRfact,tpro,dpro(ispec))
else
dn(ispec) = dmissing
endif
enddo

! Mass density
if (specflag(1)) then
dn(1) = dot_product(dn,masswgt)
else
dn(1) = dmissing
endif

return

end subroutine msiscalc

end module msis_calc
Loading

0 comments on commit 4494bd2

Please sign in to comment.