module cam_history 117,22
!-----------------------------------------------------------------------
!
! Purpose: History module. Contains data and functions for writing history files.
!
! Public functions/subroutines:
! addfld, add_default
! intht
! write_restart_history
! read_restart_history
! outfld
! wshist
! initialize_iop_history
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
! $Id$
!-----------------------------------------------------------------------
use shr_kind_mod
, only: r8 => shr_kind_r8, r4 => shr_kind_r4
use spmd_utils
, only: masterproc, npes
use ppgrid
, only: pcols
use filenames
, only: interpret_filename_spec
use filenames
, only: ncdata, bnd_topo
use abortutils
, only: endrun
use pmgrid
, only : dyndecomp_set
use icarus_scops
, only: ntau, npres, prlim, taulim
use units
, only: getunit
use hycoef
, only: hyai, hybi, hyam, hybm, ps0
use dyn_grid
, only: get_horiz_grid_dim_d, get_horiz_grid_d, get_dyn_grid_parm
use pio, only: file_desc_t, var_desc_t, io_desc_t, &
iotype_pnetcdf, iotype_netcdf, &
pio_noerr, pio_bcast_error, pio_internal_error, &
pio_seterrorhandling, pio_setdebuglevel, pio_setframe, &
pio_rearr_box, pio_rearr_none, &
pio_nofill, pio_clobber,pio_offset, &
pio_int, pio_real, pio_double, pio_char, pio_offset, pio_unlimited, pio_global, &
pio_inq_dimid, pio_inq_dimlen, pio_inq_varid, pio_inq_varname, pio_inq_varndims, &
pio_def_dim, pio_def_var, pio_enddef, &
pio_put_att, pio_put_var, pio_get_att, pio_get_var, &
pio_write, pio_write_darray, &
pio_read_darray, pio_closefile, pio_freedecomp
use scamMod
, only: scmlon,single_column
use perf_mod
use cam_logfile
, only: iulog
use cam_pio_utils
, only: column_info, field_info, max_chars, &
max_fieldname_len, phys_decomp, dyn_decomp, dyn_stagger_decomp, &
fieldname_suffix_len, fieldname_lenp2, fieldname_len, max_string_len
implicit none
PRIVATE
public :: phys_decomp, dyn_decomp, dyn_stagger_decomp, fieldname_len ! forward from cam_pio_utils
integer, parameter :: pflds = 750 ! max number of fields for namelist entries fincl and fexcl
! also used in write restart
integer, parameter :: ptapes = 7 ! max number of tapes
! This value is used as both the _FillValue and the missing_value in NetCDF
! output.
real(r8), parameter :: fillvalue = 1.e36_r8
real(r4), parameter :: fillvalue_r4 = 1.e36_r8
! field_info structure moved to cam_pio_utils
!
! master_entry: elements of an entry in the master field list
!
type master_entry
type (field_info) :: field ! field information
character(len=1) :: avgflag(ptapes) ! averaging flag
character(len=max_chars) :: time_op(ptapes) ! time operator (e.g. max, min, avg)
logical :: act_sometape ! Field is active on some tape
logical :: actflag(ptapes) ! Per tape active/inactive flag
integer :: htapeindx(ptapes)! This field's index on particular history tape
type(master_entry), pointer :: next_entry ! The next master entry
end type master_entry
type (master_entry), pointer :: masterlinkedlist ! master field linkedlist top
type master_list
type(master_entry), pointer :: thisentry
end type master_list
type (master_list), pointer :: masterlist(:) ! master field array for hash lookup of field
!
! hbuffer_2d, hbuffer_3d: 2-D and 3-D history buffer pointers.
! Select either r4 or r8 kind buffer depending on hbuf_prec.
!
type hbuffer_2d
real(r8), pointer :: buf8(:,:) ! 2-D history buffer for r8
real(r4), pointer :: buf4(:,:) ! 2-D history buffer for r4
end type hbuffer_2d
type hbuffer_3d
real(r8), pointer :: buf8(:,:,:) ! 3-D history buffer for r8
real(r4), pointer :: buf4(:,:,:) ! 3-D history buffer for r4
end type hbuffer_3d
!
! arrays served as targets for history pointers
!
integer, target :: nothing_int(1,1) ! 2-D integer target
real(r8), target :: nothing_r8(1,1,1) ! 3-D r8 target
real(r4), target :: nothing_r4(1,1,1) ! 3-D r4 target
! column_info structure has moved to cam_pio_utils
!
! hentry: elements of an entry in the list of active fields on a single history file
!
type hentry
type (field_info) :: field ! field information
character(len=1) :: avgflag ! averaging flag
character(len=max_chars) :: time_op ! time operator (e.g. max, min, avg)
character(len=max_chars),pointer :: field_column_name(:) ! names of column groups written to tape
integer :: hbuf_prec ! history buffer precision
integer :: hwrt_prec ! history output precision
type (hbuffer_3d) :: hbuf ! history buffer
type(var_desc_t), pointer :: varid(:) ! variable ids
integer, pointer :: nacs(:,:) ! accumulation counter
type(var_desc_t) :: nacs_varid
end type hentry
!
! active_entry: vehicle for producing a ragged array
!
type active_entry
type(hentry), pointer :: hlist(:)
type (column_info),pointer :: column (:) ! array of history tape column entries
type (column_info),pointer :: column_st(:) ! array of history tape column entries for staggered grid (FV)
!
! PIO ids
!
type(file_desc_t) :: File ! PIO file id
type(var_desc_t) :: mdtid ! var id for timestep
type(var_desc_t) :: ndbaseid ! var id for base day
type(var_desc_t) :: nsbaseid ! var id for base seconds of base day
type(var_desc_t) :: nbdateid ! var id for base date
type(var_desc_t) :: nbsecid ! var id for base seconds of base date
type(var_desc_t) :: ndcurid ! var id for current day
type(var_desc_t) :: nscurid ! var id for current seconds of current day
type(var_desc_t) :: dateid ! var id for current date
type(var_desc_t) :: co2vmrid ! var id for co2 volume mixing ratio
type(var_desc_t) :: ch4vmrid ! var id for ch4 volume mixing ratio
type(var_desc_t) :: n2ovmrid ! var id for n2o volume mixing ratio
type(var_desc_t) :: f11vmrid ! var id for f11 volume mixing ratio
type(var_desc_t) :: f12vmrid ! var id for f12 volume mixing ratio
type(var_desc_t) :: sol_tsiid ! var id for total solar irradiance (W/m2)
type(var_desc_t) :: datesecid ! var id for curent seconds of current date
#if ( defined BFB_CAM_SCAM_IOP )
type(var_desc_t) :: bdateid ! var id for base date
type(var_desc_t) :: tsecid ! var id for curent seconds of current date
#endif
type(var_desc_t) :: nstephid ! var id for current timestep
type(var_desc_t) :: timeid ! var id for time
type(var_desc_t) :: tbndid ! var id for time_bnds
type(var_desc_t) :: date_writtenid ! var id for date time sample written
type(var_desc_t) :: time_writtenid ! var id for time time sample written
type(var_desc_t) :: nlonid ! var id for number of longitudes
type(var_desc_t) :: wnummaxid ! var id for cutoff fourier wavenumber (reduced grid)
end type active_entry
type (active_entry), pointer :: tape(:) ! history tapes
type (active_entry), target,allocatable :: history_tape(:) ! history tapes
type (active_entry), target, allocatable :: restarthistory_tape(:) ! restart history tapes
type rvar_id
type(var_desc_t), pointer :: vdesc
integer :: type
integer :: ndims
integer :: dims(4)
character(len=fieldname_lenp2) :: name
end type rvar_id
type rdim_id
integer :: len
integer :: dimid
character(len=fieldname_lenp2) :: name
end type rdim_id
integer, parameter :: restartvarcnt=27
integer, parameter :: restartdimcnt=7
type(rvar_id) :: restartvars(restartvarcnt)
type(rdim_id) :: restartdims(restartdimcnt)
!
! dim_index_2d, dim_index_3d: 2-D & 3-D dimension index lower & upper bounds
!
type dim_index_2d ! 2-D dimension index
integer :: beg1, end1 ! lower & upper bounds of 1st dimension
integer :: beg2, end2 ! lower & upper bounds of 2nd dimension
end type dim_index_2d
type dim_index_3d ! 3-D dimension index
integer :: beg1, end1 ! lower & upper bounds of 1st dimension
integer :: beg2, end2 ! lower & upper bounds of 2nd dimension
integer :: beg3, end3 ! lower & upper bounds of 3rd dimension
end type dim_index_3d
integer :: nfmaster = 0 ! number of fields in master field list
integer :: nflds(ptapes) ! number of fields per tape
! per tape sampling frequency (0=monthly avg)
integer :: i ! index for nhtfrq initialization
integer :: nhtfrq(ptapes) = (/0, (-24, i=2,ptapes)/) ! history write frequency (0 = monthly)
integer :: mfilt(ptapes) = 30 ! number of time samples per tape
integer :: nfils(ptapes) ! Array of no. of files on current h-file
integer :: ngroup(ptapes) ! Array of no. of contiguous columns on current h-file
integer :: mtapes = 0 ! index of max history file requested
integer :: nexcl(ptapes) ! Actual number of excluded fields
integer :: nincl(ptapes) ! Actual number of included primary file fields
integer :: nhstpr(ptapes) = 8 ! history buffer precision (8 or 4 bytes)
integer :: ndens(ptapes) = 2 ! packing density (double (1) or real (2))
integer :: ncprec(ptapes) = -999 ! netcdf packing parameter based on ndens
real(r8) :: beg_time(ptapes) ! time at beginning of an averaging interval
!
! Netcdf ids
!
type(var_desc_t) :: gwid ! var id for gaussian weights
integer :: nscurf(ptapes) ! First "current" second of day for each h-file
integer :: ncsecf(ptapes) ! First "current" second of date for each h-file
logical :: rgnht(ptapes) = .false. ! flag array indicating regeneration volumes
logical :: hstwr(ptapes) = .false. ! Flag for history writes
logical :: empty_htapes = .false. ! Namelist flag indicates no default history fields
logical :: htapes_defined = .false. ! flag indicates history contents have been defined
integer :: luhrest = -1 ! history restart file unit number
character(len=max_string_len) :: hrestpath(ptapes) = (/(' ',i=1,ptapes)/) ! Full history restart pathnames
character(len=max_string_len) :: nfpath(ptapes) = (/(' ',i=1,ptapes)/) ! Array of first pathnames, for header
character(len=max_string_len) :: cpath(ptapes) ! Array of current pathnames
character(len=max_string_len) :: nhfil(ptapes) ! Array of current file names
character(len=1) :: avgflag_pertape(ptapes) = (/(' ',i=1,ptapes)/) ! per tape averaging flag
character(len=8) :: logname ! user name
character(len=16) :: host ! host name
character(len=max_string_len) :: ctitle = ' ' ! Case title
character(len=8) :: inithist = 'YEARLY' ! If set to '6-HOURLY, 'DAILY', 'MONTHLY' or
! 'YEARLY' then write IC file
character(len=fieldname_lenp2) :: fincl(pflds,ptapes) ! List of fields to add to primary h-file
character(len=max_chars) :: fincllonlat(pflds,ptapes) ! List of fields to add to primary h-file
character(len=fieldname_lenp2) :: fexcl(pflds,ptapes) ! List of fields to rm from primary h-file
character(len=fieldname_lenp2) :: fhstpr(pflds,ptapes) ! List of fields to change default hbuf size
character(len=fieldname_lenp2) :: fwrtpr(pflds,ptapes) ! List of fields to change default history output prec
character(len=fieldname_suffix_len ) :: fieldname_suffix = '&IC' ! Suffix appended to field names for IC file
integer :: plon, plat, plev, plevp
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! Hashing.
!
! Accelerate outfld processing by using a hash function of the field name
! to index masterlist and determine whehter the particular field is to
! be written to any history tape.
!
!
! Note: the outfld hashing logic will fail if any of the following are true:
!
! 1) The lower bound on the dimension of 'masterlist' is less than 1.
!
! 2) 'outfld' is called with field names that are not defined on
! masterlist. This applies to both initial/branch and restart
! runs.
!
! 3) An inconsistency between a field's tape active flag
! 'masterlist(ff)%actflag(t)' and active fields read from
! restart files.
!
! 4) Invoking function 'gen_hash_key' before the primary and secondary
! hash tables have been created (routine bld_outfld_hash_tbls).
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! User definable constants for hash and overflow tables.
! Define size of primary hash table (specified as 2**size).
!
integer, parameter :: tbl_hash_pri_sz_lg2 = 16
!
! Define size of overflow hash table % of primary hash table.
!
integer, parameter :: tbl_hash_oflow_percent = 20
!
! Do *not* modify the parameters below.
!
integer, parameter :: tbl_hash_pri_sz = 2**tbl_hash_pri_sz_lg2
integer, parameter :: tbl_hash_oflow_sz = tbl_hash_pri_sz * (tbl_hash_oflow_percent/100.0_r8)
!
! The primary and overflow tables are organized to mimimize space (read:
! try to maximimze cache line usage).
!
! gen_hash_key(fieldname) will return an index on the interval
! [0 ... tbl_hash_pri_sz-1].
!
!
! Primary:
! gen_hash_key(fieldname)-------+ +----------+
! | | -ii | 1 ------>tbl_hash_oflow(ii)
! | +----------+
! +--> | ff | 2 ------>masterlist(ff)
! +----------+
! | | ...
! +----------+
! | | tbl_hash_pri_sz
! +----------+
!
! Overflow (if tbl_hash_pri() < 0):
! tbl_hash_pri(gen_hash_key(fieldname))
! |
! | +----------+
! | | 1 | 1 (one entry on O.F. chain)
! | +----------+
! | | ff_m | 2
! | +----------+
! +---------> | 3 | 3 (three entries on chain)
! +----------+
! | ff_x | 4
! +----------+
! | ff_y | 5
! +----------+
! | ff_z | 6
! +----------+
! | | ...
! +----------+
! | | tbl_hash_oflow_sz
! +----------+
!
!
integer, dimension(0:tbl_hash_pri_sz-1) :: tbl_hash_pri ! Primary hash table
integer, dimension(tbl_hash_oflow_sz) :: tbl_hash_oflow ! Overflow hash table
!
! Constants used in hashing function gen_hash_key.
! Note: if the constants in table 'tbl_gen_hash_key' below are modified,
! changes are required to routine 'gen_hash_key' because of specific
! logic in the routine that optimizes character strings of length 8.
!
integer, parameter :: gen_hash_key_offset = z'000053db'
integer, parameter :: tbl_max_idx = 15 ! 2**N - 1
integer, dimension(0:tbl_max_idx) :: tbl_gen_hash_key = &
(/61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1/)
!
! Overloading assignment operator
!
interface assignment (=)
module procedure hbuf_assigned_to_hbuf
module procedure hbuf_assigned_to_real8
end interface
!
! Generic procedures
!
interface allocate_hbuf 2
module procedure allocate_hbuf2d
module procedure allocate_hbuf3d
end interface
interface deallocate_hbuf
module procedure deallocate_hbuf2d
module procedure deallocate_hbuf3d
end interface
interface nullify_hbuf
module procedure nullify_hbuf2d
module procedure nullify_hbuf3d
end interface
!
! Public entities
!
!
! Filename specifiers for history, initial files and restart history files
! (%c = caseid, $y = year, $m = month, $d = day, $s = seconds in day, %t = tape number)
!
character(len=max_string_len) :: rhfilename_spec = '%c.cam2.rh%t.%y-%m-%d-%s.nc' ! history restart
character(len=max_string_len), public :: hfilename_spec(ptapes) = (/ (' ', i=1, ptapes) /) ! filename specifyer
! To allow parameterizations to initialize arrays to the fillvalue
! THIS NEEDS TO BE FIXED. No parameterization should be allowed access to fillvalue
public :: fillvalue
! needed by history_default
public :: init_masterlinkedlist
! Needed by initext
public :: nhtfrq, mfilt, inithist, ctitle
! Needed by read_namelist
public :: fincl, fincllonlat, fexcl, fhstpr, fwrtpr
public :: pflds, ptapes, empty_htapes, nhstpr, ndens
public :: avgflag_pertape
! Needed by stepon
public :: hstwr
public :: nfils
! Functions
public :: init_restart_history ! Write restart history data
public :: write_restart_history ! Write restart history data
public :: read_restart_history ! Read restart history data
public :: wshist ! Write files out
public :: outfld ! Output a field
public :: intht ! Initialization
public :: wrapup ! process history files at end of run
public :: write_inithist ! logical flag to allow dump of IC history buffer to IC file
public :: addfld ! Add a field to history file
public :: add_default ! Add the default fields
public :: get_hfilepath ! Return history filename
public :: get_mtapes ! Return the number of tapes being used
public :: get_hist_restart_filepath ! Return the full filepath to the history restart file
public :: hist_fld_active ! Determine if a field is active on any history file
CONTAINS
subroutine init_masterlinkedlist() 1
! integer :: t
nullify(masterlinkedlist)
nullify(tape)
end subroutine init_masterlinkedlist
subroutine intht () 1,21
!
!-----------------------------------------------------------------------
!
! Purpose: Initialize history file handler for initial or continuation run.
! For example, on an initial run, this routine initializes "mtapes"
! history files. On a restart or regeneration run, this routine
! only initializes history files declared beyond what existed on the
! previous run. Files which already existed on the previous run have
! already been initialized (i.e. named and opened) in routine RESTRT.
!
! Method: Loop over tapes and fields per tape setting appropriate variables and
! calling appropriate routines
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
use ioFileMod
use shr_sys_mod
, only: shr_sys_getenv
use time_manager
, only: get_prev_time, get_curr_time
use cam_control_mod
, only : nsrest
#if (defined SPMD)
use spmd_utils
, only : mpichar, mpicom
#endif
!
!-----------------------------------------------------------------------
!
! Local workspace
!
integer :: t, f ! tape, field indices
integer :: begdim1 ! on-node dim1 start index
integer :: enddim1 ! on-node dim1 end index
integer :: begdim2 ! on-node dim2 start index
integer :: enddim2 ! on-node dim2 end index
integer :: begdim3 ! on-node chunk or lat start index
integer :: enddim3 ! on-node chunk or lat end index
type (dim_index_3d) :: dimind ! 3-D dimension index
integer :: day, sec ! day and seconds from base date
integer :: i ! index
integer :: rcode ! shr_sys_getenv return code
type(master_entry), pointer :: listentry
!
! Print master field list
!
plon = get_dyn_grid_parm
('plon')
plat = get_dyn_grid_parm
('plat')
plev = get_dyn_grid_parm
('plev')
plevp = get_dyn_grid_parm
('plevp')
if (masterproc) then
write(iulog,*)' ******* MASTER FIELD LIST *******'
end if
listentry=>masterlinkedlist
f=0
do while(associated(listentry))
f=f+1
if(masterproc) then
write(iulog,9000)f,listentry%field%name,listentry%field%units
9000 format (i5,1x,a19,1x,a8)
end if
listentry=>listentry%next_entry
end do
nfmaster = f
if(masterproc) write(iulog,*)'BLDFLD:nfmaster=',nfmaster
!
! Now that masterlinkedlist is defined and we are performing a restart run
! (htapes_defined == .true.), construct primary and secondary hashing tables.
!
if ( htapes_defined ) then
call bld_outfld_hash_tbls
()
call bld_htapefld_indices
()
return
end if
!
! Get users logname and machine hostname
!
if ( masterproc )then
logname = ' '
call shr_sys_getenv
('LOGNAME',logname,rcode)
host = ' '
call shr_sys_getenv
('HOST',host,rcode)
end if
#ifdef SPMD
! PIO requires netcdf attributes have consistant values on all tasks
call mpibcast
(logname, len(logname), mpichar, 0, mpicom)
call mpibcast
(host, len(host), mpichar, 0, mpicom)
#endif
!
! Override averaging flag for all fields on a particular tape if namelist input so specifies
!
do t=1,ptapes
if (avgflag_pertape(t) /= ' ') then
call h_override
(t)
end if
end do
!
! Define field list information for all history files.
! Update mtapes to reflect *current* number of history files (note,
! restart and regen runs can have additional auxiliary history files
! declared).
!
call fldlst
()
!
! Loop over max. no. of history files permitted
!
if ( nsrest .eq. 3 ) then
call get_prev_time
(day, sec) ! elapased time since reference date
else
call get_curr_time
(day, sec) ! elapased time since reference date
end if
do t=1,mtapes
nfils(t) = 0 ! no. of time samples in hist. file no. t
! Time at beginning of current averaging interval.
beg_time(t) = day + sec/86400._r8
end do
!
! Check that the number of history files declared does not exceed
! the maximum allowed.
!
if (mtapes > ptapes) then
write(iulog,*) 'INTHT: Too many history files declared, max=',ptapes
write(iulog,*)'To increase, change parameter ptapes.'
call endrun
end if
!
! Initialize history variables
!
do t=1,mtapes
do f=1,nflds(t)
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
dimind = dim_index_3d (begdim1,enddim1,begdim2,enddim2,begdim3,enddim3)
call allocate_hbuf
(tape(t)%hlist(f)%hbuf,dimind,tape(t)%hlist(f)%hbuf_prec)
tape(t)%hlist(f)%hbuf = 0._r8
if(tape(t)%hlist(f)%field%flag_xyfill) then
allocate (tape(t)%hlist(f)%nacs(enddim1-begdim1+1,begdim3:enddim3))
else
allocate (tape(t)%hlist(f)%nacs(1,begdim3:enddim3))
end if
tape(t)%hlist(f)%nacs(:,:)=0
end do
end do
return
end subroutine intht
subroutine restart_vars_setnames() 1
restartvars(1)%name = 'rgnht'
restartvars(1)%type = pio_int
restartvars(1)%ndims = 1
restartvars(1)%dims(1) = 1 ! mtapes
restartvars(2)%name = 'nhtfrq'
restartvars(2)%type = pio_int
restartvars(2)%ndims = 1
restartvars(2)%dims(1) = 1 ! mtapes
restartvars(3)%name = 'nflds'
restartvars(3)%type = pio_int
restartvars(3)%ndims = 1
restartvars(3)%dims(1) = 1 ! mtapes
restartvars(4)%name = 'nfils'
restartvars(4)%type = pio_int
restartvars(4)%ndims = 1
restartvars(4)%dims(1) = 1 ! mtapes
restartvars(5)%name = 'mfilt'
restartvars(5)%type = pio_int
restartvars(5)%ndims = 1
restartvars(5)%dims(1) = 1 ! mtapes
restartvars(6)%name = 'nfpath'
restartvars(6)%type = pio_char
restartvars(6)%ndims = 2
restartvars(6)%dims(1) = 2 ! max_string_len
restartvars(6)%dims(2) = 1 ! mtapes
restartvars(7)%name = 'cpath'
restartvars(7)%type = pio_char
restartvars(7)%ndims = 2
restartvars(7)%dims(1) = 2 ! max_string_len
restartvars(7)%dims(2) = 1 ! mtapes
restartvars(8)%name = 'nhfil'
restartvars(8)%type = pio_char
restartvars(8)%ndims = 2
restartvars(8)%dims(1) = 2 ! max_string_len
restartvars(8)%dims(2) = 1 ! mtapes
restartvars(9)%name = 'nhstpr'
restartvars(9)%type = pio_int
restartvars(9)%ndims = 1
restartvars(9)%dims(1) = 1 ! mtapes
restartvars(10)%name = 'ndens'
restartvars(10)%type = pio_int
restartvars(10)%ndims = 1
restartvars(10)%dims(1) = 1 ! mtapes
restartvars(11)%name = 'ncprec'
restartvars(11)%type = pio_int
restartvars(11)%ndims = 1
restartvars(11)%dims(1) = 1 ! mtapes
restartvars(12)%name = 'ngroup'
restartvars(12)%type = pio_int
restartvars(12)%ndims = 1
restartvars(12)%dims(1) = 1 ! mtapes
restartvars(13)%name = 'beg_time'
restartvars(13)%type = pio_double
restartvars(13)%ndims = 1
restartvars(13)%dims(1) = 1 ! mtapes
restartvars(14)%name = 'fincl'
restartvars(14)%type = pio_char
restartvars(14)%ndims = 3
restartvars(14)%dims(1) = 3 ! fieldname_lenp2
restartvars(14)%dims(2) = 4 ! pflds
restartvars(14)%dims(3) = 1 ! mtapes
restartvars(15)%name = 'fexcl'
restartvars(15)%type = pio_char
restartvars(15)%ndims = 3
restartvars(15)%dims(1) = 3 ! fieldname_lenp2
restartvars(15)%dims(2) = 4 ! pflds
restartvars(15)%dims(3) = 1 ! mtapes
restartvars(16)%name = 'field_name'
restartvars(16)%type = pio_char
restartvars(16)%ndims = 3
restartvars(16)%dims(1) = 6 ! max_fieldname_len
restartvars(16)%dims(2) = 7 ! maxnflds
restartvars(16)%dims(3) = 1 ! mtapes
restartvars(17)%name = 'decomp_type'
restartvars(17)%type = pio_int
restartvars(17)%ndims = 2
restartvars(17)%dims(1) = 7 ! maxnflds
restartvars(17)%dims(2) = 1 ! mtapes
restartvars(18)%name = 'numlev'
restartvars(18)%type = pio_int
restartvars(18)%ndims = 2
restartvars(18)%dims(1) = 7 ! maxnflds
restartvars(18)%dims(2) = 1 ! mtapes
restartvars(19)%name = 'hrestpath'
restartvars(19)%type = pio_char
restartvars(19)%ndims = 2
restartvars(19)%dims(1) = 2 ! max_string_len
restartvars(19)%dims(2) = 1 ! mtapes
restartvars(20)%name = 'hbuf_prec'
restartvars(20)%type = pio_int
restartvars(20)%ndims = 2
restartvars(20)%dims(1) = 7 ! maxnflds
restartvars(20)%dims(2) = 1 ! mtapes
restartvars(21)%name = 'hwrt_prec'
restartvars(21)%type = pio_int
restartvars(21)%ndims = 2
restartvars(21)%dims(1) = 7 ! maxnflds
restartvars(21)%dims(2) = 1 ! mtapes
restartvars(22)%name = 'avgflag'
restartvars(22)%type = pio_char
restartvars(22)%ndims = 2
restartvars(22)%dims(1) = 7 ! maxnflds
restartvars(22)%dims(2) = 1 ! mtapes
restartvars(23)%name = 'sampling_seq'
restartvars(23)%type = pio_char
restartvars(23)%ndims = 3
restartvars(23)%dims(1) = 5 ! max_chars
restartvars(23)%dims(2) = 7 ! maxnflds
restartvars(23)%dims(3) = 1 ! mtapes
restartvars(24)%name = 'long_name'
restartvars(24)%type = pio_char
restartvars(24)%ndims = 3
restartvars(24)%dims(1) = 5 ! max_chars
restartvars(24)%dims(2) = 7 ! maxnflds
restartvars(24)%dims(3) = 1 ! mtapes
restartvars(25)%name = 'units'
restartvars(25)%type = pio_char
restartvars(25)%ndims = 3
restartvars(25)%dims(1) = 5 ! max_chars
restartvars(25)%dims(2) = 7 ! maxnflds
restartvars(25)%dims(3) = 1 ! mtapes
restartvars(26)%name = 'flags'
restartvars(26)%type = pio_int
restartvars(26)%ndims = 2
restartvars(26)%dims(1) = 7 ! maxnflds
restartvars(26)%dims(2) = 1 ! mtapes
restartvars(27)%name = 'fincllonlat'
restartvars(27)%type = pio_char
restartvars(27)%ndims = 3
restartvars(27)%dims(1) = 5 ! max_chars
restartvars(27)%dims(2) = 4 ! pflds
restartvars(27)%dims(3) = 1 ! mtapes
end subroutine restart_vars_setnames
subroutine restart_dims_setnames() 1
restartdims(1)%name = 'mtapes'
restartdims(1)%len = mtapes
restartdims(2)%name = 'max_string_len'
restartdims(2)%len = max_string_len
restartdims(3)%name = 'fieldname_lenp2'
restartdims(3)%len = fieldname_lenp2
restartdims(4)%name = 'pflds'
restartdims(4)%len = pflds
restartdims(5)%name = 'max_chars'
restartdims(5)%len = max_chars
restartdims(6)%name = 'max_fieldname_len'
restartdims(6)%len = max_fieldname_len
restartdims(7)%name = 'maxnflds'
restartdims(7)%len = maxval(nflds)
end subroutine restart_dims_setnames
subroutine init_restart_history (File) 1,2
!--------------------------------------------------------------------------------------------------
!
! Arguments
!
type(file_desc_t), intent(inout) :: File ! Pio file Handle
!
! Local
!
integer :: dimids(4), ndims
integer :: ierr, i, k
! Don't need to write restart data if we have written the file this step
where (hstwr(:))
rgnht(:) = .false.
elsewhere
rgnht(:) = .true.
end where
if(mtapes>0) then
call restart_vars_setnames
()
call restart_dims_setnames
()
call pio_seterrorhandling(File, PIO_BCAST_ERROR)
do i=1,restartdimcnt
! it's possible that one or more of these have been defined elsewhere
ierr = pio_inq_dimid(File,restartdims(i)%name, restartdims(i)%dimid)
if(ierr/=PIO_NOERR) then
ierr = pio_def_dim(File,restartdims(i)%name, restartdims(i)%len, &
restartdims(i)%dimid)
end if
end do
call pio_seterrorhandling(File, PIO_INTERNAL_ERROR)
do i=1,restartvarcnt
ndims= restartvars(i)%ndims
do k=1,ndims
dimids(k)=restartdims(restartvars(i)%dims(k))%dimid
end do
allocate(restartvars(i)%vdesc)
ierr = pio_def_var(File, restartvars(i)%name, restartvars(i)%type, dimids(1:ndims), restartvars(i)%vdesc)
end do
end if
end subroutine init_restart_history
function restartvar_getdesc(name) result(vdesc) 27,1
character(len=*), intent(in) :: name
type(var_desc_t), pointer :: vdesc
character(len=max_chars) :: errmsg
integer :: i
nullify(vdesc)
do i=1,restartvarcnt
if(name .eq. restartvars(i)%name) then
vdesc=>restartvars(i)%vdesc
exit
end if
end do
if(.not.associated(vdesc)) then
errmsg = 'Could not find restart variable '//name
call endrun
(errmsg)
end if
end function restartvar_getdesc
!#######################################################################
subroutine write_restart_history ( File, & 2,29
yr_spec, mon_spec, day_spec, sec_spec )
implicit none
!--------------------------------------------------------------------------------------------------
!
! Arguments
!
type(file_desc_t), intent(inout) :: file ! PIO restart file pointer
integer, intent(in), optional :: yr_spec ! Simulation year
integer, intent(in), optional :: mon_spec ! Simulation month
integer, intent(in), optional :: day_spec ! Simulation day
integer, intent(in), optional :: sec_spec ! Seconds into current simulation day
!
! Local workspace
!
integer :: ierr, t, f
integer :: rgnht_int(ptapes), start(2), count(2), startc(3)
type(var_desc_t), pointer :: vdesc
type(var_desc_t), pointer :: field_name_desc, decomp_type_desc, numlev_desc, &
hrestpath_desc, hbuf_prec_desc, avgflag_desc, sseq_desc, longname_desc, units_desc, hwrt_prec_desc, flag_desc
integer, allocatable :: flags(:,:)
integer :: maxnflds
maxnflds = maxval(nflds)
allocate(flags(maxnflds, mtapes))
flags = 0
!
!-----------------------------------------------------------------------
! Write the history restart data if necessary
!-----------------------------------------------------------------------
rgnht_int(:) = 0
if(.not.allocated(restarthistory_tape)) allocate(restarthistory_tape(mtapes))
do t=1,mtapes
! No need to write history IC restart because it is always instantaneous
if (is_initfile
(file_index=t)) rgnht(t) = .false.
! No need to write restart data for empty files
if (nflds(t) == 0) rgnht(t) = .false.
if(rgnht(t)) then
rgnht_int(t) = 1
restarthistory_tape(t)%hlist => history_tape(t)%hlist
if(associated(history_tape(t)%column)) then
restarthistory_tape(t)%column => history_tape(t)%column
end if
if(associated(history_tape(t)%column_st)) then
restarthistory_tape(t)%column_st => history_tape(t)%column_st
end if
end if
end do
if(mtapes<=0) return
call wshist
(rgnht)
vdesc => restartvar_getdesc
('fincl')
ierr= pio_put_var(File, vdesc, fincl(:,1:mtapes))
vdesc => restartvar_getdesc
('fincllonlat')
ierr= pio_put_var(File, vdesc, fincllonlat(:,1:mtapes))
vdesc => restartvar_getdesc
('fexcl')
ierr= pio_put_var(File, vdesc, fexcl(:,1:mtapes))
vdesc => restartvar_getdesc
('rgnht')
ierr= pio_put_var(File, vdesc, rgnht_int(1:mtapes))
vdesc => restartvar_getdesc
('nhtfrq')
ierr= pio_put_var(File, vdesc, nhtfrq(1:mtapes))
vdesc => restartvar_getdesc
('nflds')
ierr= pio_put_var(File, vdesc, nflds(1:mtapes))
vdesc => restartvar_getdesc
('nfils')
ierr= pio_put_var(File, vdesc, nfils(1:mtapes))
vdesc => restartvar_getdesc
('mfilt')
ierr= pio_put_var(File, vdesc, mfilt(1:mtapes))
vdesc => restartvar_getdesc
('nfpath')
ierr= pio_put_var(File, vdesc, nfpath(1:mtapes))
vdesc => restartvar_getdesc
('cpath')
ierr= pio_put_var(File, vdesc, cpath(1:mtapes))
vdesc => restartvar_getdesc
('nhfil')
ierr= pio_put_var(File, vdesc, nhfil(1:mtapes))
vdesc => restartvar_getdesc
('nhstpr')
ierr= pio_put_var(File, vdesc, nhstpr(1:mtapes))
vdesc => restartvar_getdesc
('ndens')
ierr= pio_put_var(File, vdesc, ndens(1:mtapes))
vdesc => restartvar_getdesc
('ncprec')
ierr= pio_put_var(File, vdesc, ncprec(1:mtapes))
vdesc => restartvar_getdesc
('ngroup')
ierr= pio_put_var(File, vdesc, ngroup(1:mtapes))
vdesc => restartvar_getdesc
('beg_time')
ierr= pio_put_var(File, vdesc, beg_time(1:mtapes))
vdesc => restartvar_getdesc
('hrestpath')
ierr = pio_put_var(File, vdesc, hrestpath(1:mtapes))
field_name_desc => restartvar_getdesc
('field_name')
decomp_type_desc => restartvar_getdesc
('decomp_type')
numlev_desc => restartvar_getdesc
('numlev')
hbuf_prec_desc => restartvar_getdesc
('hbuf_prec')
hwrt_prec_desc => restartvar_getdesc
('hwrt_prec')
sseq_desc => restartvar_getdesc
('sampling_seq')
longname_desc => restartvar_getdesc
('long_name')
units_desc => restartvar_getdesc
('units')
avgflag_desc => restartvar_getdesc
('avgflag')
flag_desc => restartvar_getdesc
('flags')
tape=>history_tape
startc(1)=1
do t = 1,mtapes
start(2)=t
startc(3)=t
do f=1,nflds(t)
start(1)=f
startc(2)=f
ierr = pio_put_var(File, field_name_desc,startc,tape(t)%hlist(f)%field%name)
ierr = pio_put_var(File, decomp_type_desc,start,tape(t)%hlist(f)%field%decomp_type)
ierr = pio_put_var(File, numlev_desc,start,tape(t)%hlist(f)%field%numlev)
ierr = pio_put_var(File, hbuf_prec_desc,start,tape(t)%hlist(f)%hbuf_prec)
ierr = pio_put_var(File, hwrt_prec_desc,start,tape(t)%hlist(f)%hwrt_prec)
ierr = pio_put_var(File, sseq_desc,startc,tape(t)%hlist(f)%field%sampling_seq)
ierr = pio_put_var(File, longname_desc,startc,tape(t)%hlist(f)%field%long_name)
ierr = pio_put_var(File, units_desc,startc,tape(t)%hlist(f)%field%units)
ierr = pio_put_var(File, avgflag_desc,start, tape(t)%hlist(f)%avgflag)
if(tape(t)%hlist(f)%field%flag_xyfill) flags(f,t)=10
if(tape(t)%hlist(f)%field%flag_isccplev) flags(f,t)=flags(f,t)+1
end do
end do
ierr = pio_put_var(File, flag_desc, flags)
deallocate(flags)
return
end subroutine write_restart_history
!#######################################################################
subroutine read_restart_history (File) 1,43
use ppgrid
, only: begchunk, endchunk
use phys_grid
, only: get_ncols_p, scatter_field_to_chunk_int
use rgrid
, only: nlon
use dycore
, only: dycore_is
use cam_pio_utils
, only: cam_pio_openfile, phys_decomp, dyn_decomp, dyn_stagger_decomp, get_decomp
use ioFileMod
, only: getfil
use shr_sys_mod
, only: shr_sys_getenv
#if (defined SPMD)
use spmd_utils
, only : iam, mpicom, mpichar
#endif
!
!-----------------------------------------------------------------------
!
! Arguments
!
type(file_desc_t), intent(inout) :: File ! unit number
!
! Local workspace
!
integer t, f ! tape, field indices
integer c ! chunk or lat index
integer lenc ! length of useful character data
integer numlev ! number of vertical levels (dimension and loop)
integer begdim2 ! on-node vert start index
integer enddim2 ! on-node vert end index
integer ioerr ! error code from read()
integer begdim1 ! on-node dim1 start index
integer enddim1 ! on-node dim1 end index
integer begdim3 ! on-node chunk or lat start index
integer enddim3 ! on-node chunk or lat end index
integer ncol ! number of active columns per chunk
integer lenarr ! global size of array to be read
integer flag_xyfill_int
integer flag_isccplev_int
integer rgnht_int(ptapes)
integer :: ierr
character(len=max_string_len) :: locfn ! Local filename
character(len=max_fieldname_len), allocatable :: tmpname(:,:)
integer, allocatable :: decomp(:,:), tmpnumlev(:,:)
integer, pointer :: nacs(:,:) ! accumulation counter
character(len=max_fieldname_len) :: fname_tmp ! local copy of field name
character(len=max_chars), pointer :: tmpstr
type (hbuffer_3d) :: hbuf ! history buffer
type (hbuffer_3d) :: localxyz ! xyz buffer (local)
real(r8) :: sum_r8
real(r4) :: sum_r4
type (dim_index_3d) :: dimind, dimind_xyz, dimind_xzy ! 3-D dimension index
integer :: ii, i, j, k, mtapes_dimid, vsize
integer :: beglatxy, endlatxy, beglat, endlat, beglonxy, endlonxy, beglon, endlon
type(var_desc_t) :: vdesc, longname_desc, units_desc, avgflag_desc, sseq_desc
type(io_desc_t), pointer :: iodesc
real(r8), allocatable :: tmpfldr8(:)
real(r4), allocatable :: tmpfldr4(:)
integer, allocatable :: tmpfldi(:), tmpprec(:,:,:)
character(len=1), allocatable:: tmpavgflag(:, :)
integer, allocatable :: flags(:,:)
integer :: nacsdimcnt, nacsval
integer :: maxnflds, maxnflds_dimid
!
! Get users logname and machine hostname
!
if ( masterproc )then
logname = ' '
call shr_sys_getenv
('LOGNAME',logname,ierr)
host = ' '
call shr_sys_getenv
('HOST',host,ierr)
end if
#ifdef SPMD
! PIO requires netcdf attributes have consistant values on all tasks
call mpibcast
(logname, len(logname), mpichar, 0, mpicom)
call mpibcast
(host, len(host), mpichar, 0, mpicom)
#endif
beglonxy = get_dyn_grid_parm
('beglonxy')
endlonxy = get_dyn_grid_parm
('endlonxy')
beglatxy = get_dyn_grid_parm
('beglatxy')
endlatxy = get_dyn_grid_parm
('endlatxy')
beglat = get_dyn_grid_parm
('beglat')
endlat = get_dyn_grid_parm
('endlat')
beglon = get_dyn_grid_parm
('beglon')
endlon = get_dyn_grid_parm
('endlon')
plev = get_dyn_grid_parm
('plev')
plevp = get_dyn_grid_parm
('plevp')
call get_horiz_grid_dim_d
(plon, plat)
call pio_seterrorhandling(File, PIO_BCAST_ERROR)
ierr = pio_inq_dimid(File, 'mtapes', mtapes_dimid)
if(ierr/= PIO_NOERR) then
if(masterproc) write(iulog,*) 'Not reading history info from restart file', ierr, file%fh
return ! no history info in restart file
end if
call pio_seterrorhandling(File, PIO_INTERNAL_ERROR)
ierr = pio_inq_dimlen(File, mtapes_dimid, mtapes)
ierr = pio_inq_dimid(File, 'maxnflds', maxnflds_dimid)
ierr = pio_inq_dimlen(File, maxnflds_dimid, maxnflds)
ierr = pio_inq_varid(File, 'rgnht', vdesc)
ierr = pio_get_var(File, vdesc, rgnht_int(1:mtapes))
ierr = pio_inq_varid(File, 'nhtfrq', vdesc)
ierr = pio_get_var(File, vdesc, nhtfrq(1:mtapes))
ierr = pio_inq_varid(File, 'nflds', vdesc)
ierr = pio_get_var(File, vdesc, nflds(1:mtapes))
ierr = pio_inq_varid(File, 'nfils', vdesc)
ierr = pio_get_var(File, vdesc, nfils(1:mtapes))
ierr = pio_inq_varid(File, 'mfilt', vdesc)
ierr = pio_get_var(File, vdesc, mfilt(1:mtapes))
ierr = pio_inq_varid(File, 'nfpath', vdesc)
ierr = pio_get_var(File, vdesc, nfpath(1:mtapes))
ierr = pio_inq_varid(File, 'cpath', vdesc)
ierr = pio_get_var(File, vdesc, cpath(1:mtapes))
ierr = pio_inq_varid(File, 'nhfil', vdesc)
ierr = pio_get_var(File, vdesc, nhfil(1:mtapes))
ierr = pio_inq_varid(File, 'hrestpath', vdesc)
ierr = pio_get_var(File, vdesc, hrestpath(1:mtapes))
ierr = pio_inq_varid(File, 'nhstpr', vdesc)
ierr = pio_get_var(File, vdesc, nhstpr(1:mtapes))
ierr = pio_inq_varid(File, 'ndens', vdesc)
ierr = pio_get_var(File, vdesc, ndens(1:mtapes))
ierr = pio_inq_varid(File, 'ncprec', vdesc)
ierr = pio_get_var(File, vdesc, ncprec(1:mtapes))
ierr = pio_inq_varid(File, 'ngroup', vdesc)
ierr = pio_get_var(File, vdesc, ngroup(1:mtapes))
ierr = pio_inq_varid(File, 'beg_time', vdesc)
ierr = pio_get_var(File, vdesc, beg_time(1:mtapes))
ierr = pio_inq_varid(File, 'fincl', vdesc)
ierr = pio_get_var(File, vdesc, fincl(:,1:mtapes))
ierr = pio_inq_varid(File, 'fincllonlat', vdesc)
ierr = pio_get_var(File, vdesc, fincllonlat(:,1:mtapes))
ierr = pio_inq_varid(File, 'fexcl', vdesc)
ierr = pio_get_var(File, vdesc, fexcl(:,1:mtapes))
allocate(tmpname(maxnflds, mtapes), decomp(maxnflds, mtapes), tmpnumlev(maxnflds,mtapes))
ierr = pio_inq_varid(File, 'field_name', vdesc)
ierr = pio_get_var(File, vdesc, tmpname)
ierr = pio_inq_varid(File, 'decomp_type', vdesc)
ierr = pio_get_var(File, vdesc, decomp)
ierr = pio_inq_varid(File, 'numlev', vdesc)
ierr = pio_get_var(File, vdesc, tmpnumlev)
allocate(tmpprec(maxnflds,mtapes,2))
ierr = pio_inq_varid(File, 'hbuf_prec',vdesc)
ierr = pio_get_var(File, vdesc, tmpprec(:,:,1))
ierr = pio_inq_varid(File, 'hwrt_prec',vdesc)
ierr = pio_get_var(File, vdesc, tmpprec(:,:,2))
allocate(flags(maxnflds,mtapes))
ierr = pio_inq_varid(File, 'flags', vdesc)
ierr = pio_get_var(File, vdesc, flags)
ierr = pio_inq_varid(File, 'avgflag', avgflag_desc)
ierr = pio_inq_varid(File, 'long_name', longname_desc)
ierr = pio_inq_varid(File, 'units', units_desc)
ierr = pio_inq_varid(File, 'sampling_seq', sseq_desc)
rgnht(:)=.false.
allocate(history_tape(mtapes))
tape => history_tape
do t=1,mtapes
nullify(tape(t)%column)
nullify(tape(t)%column_st)
if(rgnht_int(t)==1) rgnht(t)=.true.
call strip_null
(nfpath(t))
call strip_null
(cpath(t))
call strip_null
(hrestpath(t))
allocate(tape(t)%hlist(nflds(t)))
do f=1,nflds(t)
ierr = pio_get_var(File,avgflag_desc, (/f,t/), tape(t)%hlist(f)%avgflag)
ierr = pio_get_var(File,longname_desc, (/1,f,t/), tape(t)%hlist(f)%field%long_name)
ierr = pio_get_var(File,units_desc, (/1,f,t/), tape(t)%hlist(f)%field%units)
tape(t)%hlist(f)%field%sampling_seq(1:max_chars) = ' '
ierr = pio_get_var(File,sseq_desc, (/1,f,t/), tape(t)%hlist(f)%field%sampling_seq)
call strip_null
(tape(t)%hlist(f)%field%sampling_seq)
if(flags(f,t)>=10) then
tape(t)%hlist(f)%field%flag_xyfill=.true.
flags(f,t)=flags(f,t)-10
else
tape(t)%hlist(f)%field%flag_xyfill=.false.
end if
if(flags(f,t)>=1) then
tape(t)%hlist(f)%field%flag_isccplev=.true.
else
tape(t)%hlist(f)%field%flag_isccplev=.false.
end if
call strip_null
(tmpname(f,t))
tape(t)%hlist(f)%field%name = tmpname(f,t)
tape(t)%hlist(f)%field%decomp_type = decomp(f,t)
tape(t)%hlist(f)%field%numlev = tmpnumlev(f,t)
tape(t)%hlist(f)%hbuf_prec = tmpprec(f,t,1)
tape(t)%hlist(f)%hwrt_prec = tmpprec(f,t,2)
end do
end do
deallocate(tmpname, tmpnumlev, tmpprec, decomp, flags)
do t=1,mtapes
do f=1,nflds(t)
numlev = tape(t)%hlist(f)%field%numlev
select case (tape(t)%hlist(f)%field%decomp_type)
case (phys_decomp)
tape(t)%hlist(f)%field%begdim2= 1
tape(t)%hlist(f)%field%enddim2= numlev
tape(t)%hlist(f)%field%begdim3 = begchunk
tape(t)%hlist(f)%field%enddim3 = endchunk
allocate (tape(t)%hlist(f)%field%colperdim3(begchunk:endchunk))
do c=begchunk,endchunk
ncol = get_ncols_p
(c)
tape(t)%hlist(f)%field%colperdim3(c) = ncol
end do
tape(t)%hlist(f)%field%begdim1 = 1
tape(t)%hlist(f)%field%enddim1 = pcols
case (dyn_decomp)
nullify(tape(t)%hlist(f)%field%colperdim3)
tape(t)%hlist(f)%field%begdim1 = beglonxy
tape(t)%hlist(f)%field%enddim1 = endlonxy
tape(t)%hlist(f)%field%begdim3 = beglatxy
tape(t)%hlist(f)%field%enddim3 = endlatxy
tape(t)%hlist(f)%field%begdim2 = 1
tape(t)%hlist(f)%field%enddim2 = numlev
if (.not. dycore_is('LR') )then
allocate (tape(t)%hlist(f)%field%colperdim3(beglat:endlat))
do c=beglat,endlat
tape(t)%hlist(f)%field%colperdim3(c) = nlon(c)
end do
end if
case (dyn_stagger_decomp)
tape(t)%hlist(f)%field%begdim1 = beglonxy
tape(t)%hlist(f)%field%enddim1 = endlonxy
tape(t)%hlist(f)%field%begdim2 = 1
tape(t)%hlist(f)%field%enddim2 = numlev
tape(t)%hlist(f)%field%begdim3 = beglatxy
tape(t)%hlist(f)%field%enddim3 = endlatxy
nullify(tape(t)%hlist(f)%field%colperdim3)
case default
write(iulog,*)'READ_RESTART_HISTORY: bad decomp_type=',tape(t)%hlist(f)%field%decomp_type
call endrun
()
end select
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
dimind = dim_index_3d (begdim1,enddim1,begdim2,enddim2,begdim3,enddim3)
call allocate_hbuf
(tape(t)%hlist(f)%hbuf,dimind,tape(t)%hlist(f)%hbuf_prec)
nullify(tape(t)%hlist(f)%varid)
nullify(tape(t)%hlist(f)%nacs)
if(tape(t)%hlist(f)%field%flag_xyfill) then
allocate(tape(t)%hlist(f)%nacs(enddim1-begdim1+1,begdim3:enddim3))
else
allocate(tape(t)%hlist(f)%nacs(1,begdim3:enddim3))
end if
! initialize all buffers to zero - this will be overwritten later by the
! data in the history restart file if it exists.
call h_zero
(f,t)
end do
end do
!
!-----------------------------------------------------------------------
! Read history restart files
!-----------------------------------------------------------------------
!
! Loop over the total number of history files declared and
! read the pathname for any history restart files
! that are present (if any). Test to see if the run is a restart run
! AND if any history buffer regen files exist (rgnht=.T.). Note, rgnht
! is preset to false, reset to true in routine WSDS if hbuf restart files
! are written and saved in the master restart file. Each history buffer
! restart file is then obtained.
! Note: some f90 compilers (e.g. SGI) complain about I/O of
! derived types which have pointer components, so explicitly read each one.
!
do t=1,mtapes
if (rgnht(t)) then
!
! Open history restart file
!
call getfil
(hrestpath(t), locfn)
call cam_pio_openfile
(tape(t)%File, locfn, 0)
!
! Read history restart file
!
do f=1,nflds(t)
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
numlev = tape(t)%hlist(f)%field%numlev
if (tape(t)%hlist(f)%hbuf_prec == 8) then
call get_decomp
(iodesc, tape(t)%hlist(f)%field, pio_double)
else
call get_decomp
(iodesc, tape(t)%hlist(f)%field, pio_real)
end if
fname_tmp = strip_suffix
(tape(t)%hlist(f)%field%name)
if(masterproc) write(iulog, *) 'Reading history variable ',fname_tmp
ierr = pio_inq_varid(tape(t)%File, fname_tmp, vdesc)
call pio_setframe(vdesc, int(1,kind=PIO_OFFSET))
if (tape(t)%hlist(f)%hbuf_prec == 8) then
call pio_read_darray(tape(t)%File, vdesc, iodesc, tape(t)%hlist(f)%hbuf%buf8, ierr)
else
call pio_read_darray(tape(t)%File, vdesc, iodesc, tape(t)%hlist(f)%hbuf%buf4, ierr)
end if
ierr = pio_inq_varid(tape(t)%File, trim(fname_tmp)//'_nacs', vdesc)
ierr = pio_inq_varndims(tape(t)%File, vdesc, nacsdimcnt)
if(nacsdimcnt>0) then
call get_decomp
(iodesc, tape(t)%hlist(f)%field, pio_int, 1)
allocate(tape(t)%hlist(f)%nacs(begdim1:enddim1,begdim3:enddim3))
nacs => tape(t)%hlist(f)%nacs(:,:)
call pio_read_darray(tape(t)%File, vdesc, iodesc, nacs, ierr)
else
allocate(tape(t)%hlist(f)%nacs(1,begdim3:enddim3))
ierr = pio_get_var(tape(t)%File, vdesc, nacsval)
tape(t)%hlist(f)%nacs(1,:)= nacsval
end if
end do
!
! Done reading this history restart file
!
call pio_closefile (tape(t)%File)
end if ! rgnht(t)
if(ngroup(t)>0) call column_init
(t, nflds(t))
end do ! end of do mtapes loop
!
! If the history files are partially complete (contain less than
! mfilt(t) time samples, then get the files and open them.)
!
! NOTE: No need to perform this operation for IC history files or empty files
!
do t=1,mtapes
if (is_initfile
(file_index=t)) then
!
! Initialize filename specifier for IC file
!
hfilename_spec(t) = '%c.cam2.i.%y-%m-%d-%s.nc'
nfils(t) = 0
else if (nflds(t) == 0) then
nfils(t) = 0
else
if (nfils(t) > 0) then
call getfil
(cpath(t), locfn)
call cam_pio_openfile
(tape(t)%File, locfn, PIO_WRITE)
call h_inquire
(t)
end if
!
! If the history file is full, close the current unit
!
if (nfils(t) >= mfilt(t)) then
if (masterproc) then
write(iulog,*)'READ_RESTART_HISTORY: nf_close(',t,')=',nhfil(t)
end if
do f=1,nflds(t)
deallocate(tape(t)%hlist(f)%varid)
nullify(tape(t)%hlist(f)%varid)
end do
call pio_closefile(tape(t)%File)
nfils(t) = 0
end if
end if
end do
!
! set flag indicating h-tape contents are now defined (needed by addfld)
!
htapes_defined = .true.
return
end subroutine read_restart_history
!#######################################################################
character(len=max_string_len) function get_hfilepath( tape ) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Return full filepath of history file for given tape number
! This allows public read access to the filenames without making
! the filenames public data.
!
!-----------------------------------------------------------------------
!
integer, intent(in) :: tape ! Tape number
get_hfilepath = cpath( tape )
end function get_hfilepath
!#######################################################################
character(len=max_string_len) function get_hist_restart_filepath( tape ) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Return full filepath of restart file for given tape number
! This allows public read access to the filenames without making
! the filenames public data.
!
!-----------------------------------------------------------------------
!
integer, intent(in) :: tape ! Tape number
get_hist_restart_filepath = hrestpath( tape )
end function get_hist_restart_filepath
!#######################################################################
integer function get_mtapes( ) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Return the number of tapes being used.
! This allows public read access to the number of tapes without making
! mtapes public data.
!
!-----------------------------------------------------------------------
!
get_mtapes = mtapes
end function get_mtapes
recursive function get_entry_by_name(listentry, name) result(entry) 8
type(master_entry), pointer :: listentry
character(len=*), intent(in) :: name ! variable name
type(master_entry), pointer :: entry
if(associated(listentry)) then
if(listentry%field%name .eq. name) then
entry => listentry
else
entry=>get_entry_by_name(listentry%next_entry, name)
end if
else
nullify(entry)
end if
end function get_entry_by_name
!#######################################################################
subroutine fldlst () 1,36
use dycore
, only : dycore_is
!
!-----------------------------------------------------------------------
!
! Purpose: Define the contents of each history file based on namelist input for initial or branch
! run, and restart data if a restart run.
!
! Method: Use arrays fincl and fexcl to modify default history tape contents.
! Then sort the result alphanumerically for later use by OUTFLD to
! allow an n log n search time.
!
!---------------------------Local variables-----------------------------
!
integer t, f ! tape, field indices
integer ff ! index into include, exclude and fprec list
character(len=fieldname_len) :: name ! field name portion of fincl (i.e. no avgflag separator)
character(len=max_fieldname_len) :: mastername ! name from masterlist field
character(len=max_chars) :: tmpcolumn_name,latlonname,latlonnamep1 ! tmp char fields
character(len=1) :: avgflag ! averaging flag
character(len=1) :: prec_acc ! history buffer precision flag
character(len=1) :: prec_wrt ! history buffer write precision flag
type (hentry) :: tmp ! temporary used for swapping
type (column_info) :: tmpcolumn ! temporary used for swapping
type(master_entry), pointer :: listentry
tape => history_tape
!
! First ensure contents of fincl, fexcl, fhstpr and fwrtpr are all valid names
!
do t=1,ptapes
f = 1
do while (f < pflds .and. fincl(f,t) /= ' ')
name = getname (fincl(f,t))
mastername=''
listentry => get_entry_by_name
(masterlinkedlist, name)
if(associated(listentry)) mastername = listentry%field%name
if (name /= mastername) then
write(iulog,*)'FLDLST: ', trim(name), ' in fincl(', f, ') not found'
call endrun
end if
f = f + 1
end do
f = 1
do while (f < pflds .and. fexcl(f,t) /= ' ')
mastername=''
listentry => get_entry_by_name
(masterlinkedlist, fexcl(f,t))
if(associated(listentry)) mastername = listentry%field%name
if (fexcl(f,t) /= mastername) then
write(iulog,*)'FLDLST: ', fexcl(f,t), ' in fexcl(', f, ') not found'
call endrun
end if
f = f + 1
end do
f = 1
do while (f < pflds .and. fhstpr(f,t) /= ' ')
name = getname (fhstpr(f,t))
mastername=''
listentry => get_entry_by_name
(masterlinkedlist, name)
if(associated(listentry)) mastername = listentry%field%name
if (name /= mastername) then
write(iulog,*)'FLDLST: ', trim(name), ' in fhstpr(', f, ') not found'
call endrun
end if
do ff=1,f-1 ! If duplicate entry is found, stop
if (trim(name) == trim(getname
(fhstpr(ff,t)))) then
write(iulog,*)'FLDLST: Duplicate field ', name, ' in fhstpr'
call endrun
end if
end do
f = f + 1
end do
f = 1
do while (f < pflds .and. fwrtpr(f,t) /= ' ')
name = getname (fwrtpr(f,t))
mastername=''
listentry => get_entry_by_name
(masterlinkedlist, name)
if(associated(listentry)) mastername = listentry%field%name
if (name /= mastername) then
write(iulog,*)'FLDLST: ', trim(name), ' in fwrtpr(', f, ') not found'
call endrun
end if
do ff=1,f-1 ! If duplicate entry is found, stop
if (trim(name) == trim(getname
(fwrtpr(ff,t)))) then
write(iulog,*)'FLDLST: Duplicate field ', name, ' in fwrtpr'
call endrun
end if
end do
f = f + 1
end do
end do
!
! If kind values r8 and r4 are identical, set accumulation precision to 8 bytes
!
if (r4 == r8 .and. any(nhstpr == 4)) then
nhstpr(:) = 8
if (masterproc) then
write(iulog,*) 'FLDLST: Set nhstpr to 8 because kind values r8 and r4 are identical'
end if
end if
nflds(:) = 0
! IC history file is to be created, set properties
if(is_initfile
()) then
hfilename_spec(ptapes) = '%c.cam2.i.%y-%m-%d-%s.nc'
ncprec(ptapes) = pio_double
nhstpr(ptapes) = 8
ndens (ptapes) = 1
mfilt (ptapes) = 1
end if
do t=1,ptapes
!
! Add the field to the tape if specified via namelist (FINCL[1-ptapes]), or if
! it is on by default and was not excluded via namelist (FEXCL[1-ptapes]).
! Also set history buffer accumulation and output precision values according
! to the values specified via namelist (FHSTPR[1-ptapes] and FWRTPR[1-ptapes])
! or, if not on the list, to the default values given by ndens(t) and
! nhstpr(t), respectively.
!
listentry => masterlinkedlist
do while(associated(listentry))
mastername = listentry%field%name
call list_index
(fincl(1,t), mastername, ff)
if (ff > 0) then
nflds(t) = nflds(t)+1
else if ((.not. empty_htapes) .or. (is_initfile
(file_index=t))) then
call list_index
(fexcl(1,t), mastername, ff)
if (ff == 0 .and. listentry%actflag(t)) then
nflds(t) = nflds(t)+1
end if
end if
listentry=>listentry%next_entry
end do
enddo
!
! Determine total number of active history tapes
!
mtapes = 0
do t=ptapes,1,-1
if (nflds(t) > 0) then
mtapes = t
exit
end if
end do
if (masterproc) then
do t=1,mtapes
if (nflds(t) == 0) then
write(iulog,*)'FLDLST: Tape ',t,' is empty'
end if
end do
endif
allocate(history_tape(mtapes))
tape=>history_tape
do t=1,mtapes
nullify(tape(t)%hlist)
nullify(tape(t)%column)
nullify(tape(t)%column_st)
! Now we have a field count and can allocate
if(nflds(t)> 0) allocate(tape(t)%hlist(nflds(t)))
do ff=1,nflds(t)
nullify(tape(t)%hlist(ff)%nacs)
nullify(tape(t)%hlist(ff)%varid)
end do
nflds(t) = 0 ! recount to support array based method
listentry => masterlinkedlist
do while(associated(listentry))
mastername = listentry%field%name
call list_index
(fhstpr(1,t), mastername, ff)
if (ff > 0) then
prec_acc = getflag
(fhstpr(ff,t))
else
prec_acc = ' '
end if
call list_index
(fwrtpr(1,t), mastername, ff)
if (ff > 0) then
prec_wrt = getflag
(fwrtpr(ff,t))
else
prec_wrt = ' '
end if
call list_index
(fincl(1,t), mastername, ff)
if (ff > 0) then
avgflag = getflag (fincl(ff,t))
call inifld
(t, listentry, avgflag, prec_acc, prec_wrt)
else if ((.not. empty_htapes) .or. (is_initfile
(file_index=t))) then
call list_index
(fexcl(1,t), mastername, ff)
if (ff == 0 .and. listentry%actflag(t)) then
call inifld
(t, listentry, ' ', prec_acc, prec_wrt)
else
listentry%actflag(t) = .false.
end if
else
listentry%actflag(t) = .false.
end if
listentry=>listentry%next_entry
end do
!
! If column output is specified make sure there are some fields defined
! for that tape
!
if (nflds(t) .eq. 0 .and. fincllonlat(1,t) .ne. ' ') then
write(iulog,*) 'FLDLST: Column output is specified for tape ',t,' but no fields defined for that tape.'
call endrun
()
else
call column_init
(t, nflds(t))
end if
!
! Specification of tape contents now complete. Sort each list of active
! entries for efficiency in OUTFLD. Simple bubble sort.
!
do f=nflds(t)-1,1,-1
do ff=1,f
if (tape(t)%hlist(ff)%field%name > tape(t)%hlist(ff+1)%field%name) then
tmp = tape(t)%hlist(ff)
tape(t)%hlist(ff ) = tape(t)%hlist(ff+1)
tape(t)%hlist(ff+1) = tmp
else if (tape(t)%hlist(ff )%field%name == tape(t)%hlist(ff+1)%field%name) then
write(iulog,*)'FLDLST: Duplicate field ', tape(t)%hlist(ff )%field%name
write(iulog,*)'t,ff,name=',t,ff,tape(t)%hlist(ff )%field%name
call endrun
end if
end do
end do
!
! Bubble sort columns check for duplicates and rebuild field_column_names in newly sorted order
!
if (ngroup(t) .gt. 0) then
do i=ngroup(t)-1,1,-1
do ff=1,i
latlonname=trim(tape(t)%column(ff)%lon_name) // "_" // trim(tape(t)%column(ff)%lat_name)
latlonnamep1=trim(tape(t)%column(ff+1)%lon_name) // "_" // trim(tape(t)%column(ff+1)%lat_name)
if (trim(latlonname) > trim(latlonnamep1)) then
tmpcolumn = tape(t)%column(ff)
tape(t)%column(ff) = tape(t)%column(ff+1)
tape(t)%column(ff+1) = tmpcolumn
if(dycore_is
('LR')) then
tmpcolumn = tape(t)%column_st(ff)
tape(t)%column_st(ff) = tape(t)%column_st(ff+1)
tape(t)%column_st(ff+1) = tmpcolumn
end if
else if (trim(latlonname) == trim(latlonnamep1)) then
write(iulog,*)'FLDLST: Duplicate column entry for tape ',t,'duplicate column name is ',trim(latlonname)
call endrun
end if
end do
end do
do f=1,nflds(t)
do i=1,ngroup(t)
if (ngroup(t) .gt. 0) then
tape(t)%hlist(f)%field_column_name(i) = trim(tape(t)%hlist(f)%field%name) // "_" // &
trim(tape(t)%column(i)%lon_name) // "_" // trim(tape(t)%column(i)%lat_name)
else
tape(t)%hlist(f)%field_column_name(1) = ' '
end if
end do
end do
end if
if (masterproc) then
if (nflds(t) > 0) then
write(iulog,*)'FLDLST: Included fields tape ',t,'=',nflds(t)
end if
do f=1,nflds(t)
if (ngroup(t) .gt. 0) then
if (f.eq.1) write(iulog,*)'Fields on this tape will be output as column data (FIELD_LON_LAT)'
do i=1,ngroup(t)
ff=(f-1)*ngroup(t)+i
write(iulog,*) ff,' ',trim(tape(t)%hlist(f)%field_column_name(i)), tape(t)%hlist(f)%field%numlev, &
' ',tape(t)%hlist(f)%avgflag
end do
else
write(iulog,*) f,' ',tape(t)%hlist(f)%field%name, tape(t)%hlist(f)%field%numlev, &
' ',tape(t)%hlist(f)%avgflag
end if
end do
end if
end do
!
! Packing density, ndens: With netcdf, only 1 (nf_double) and 2 (pio_real)
! are allowed
! Accumulation precision, nhstpr, must be either 8 (real*8) or 4 (real*4)
!
do t=1,mtapes
if (ndens(t) == 1) then
ncprec(t) = pio_double
else if (ndens(t) == 2) then
ncprec(t) = pio_real
else
call endrun
('FLDLST: ndens must be 1 or 2')
end if
if (nhstpr(t) /= 8 .and. nhstpr(t) /= 4) then
call endrun
('FLDLST: nhstpr must be 8 or 4')
end if
end do
if (masterproc) then
do t=1,mtapes
if (is_initfile
(file_index=t)) then
write(iulog,*)'History File ',t,' write frequency ',inithist,' (INITIAL CONDITIONS)'
else if (nflds(t) > 0) then
if (nhtfrq(t) == 0 .and. t == 1) then
write(iulog,*)'History File ',t,' write frequency MONTHLY'
else
write(iulog,*)'History File ',t,' write frequency ',nhtfrq(t)
end if
end if
end do
write(iulog,*) ' '
do t=1,mtapes
if (nflds(t) > 0) then
write(iulog,*) 'Filename specifier for history file ', t, ' = ', trim(hfilename_spec(t))
end if
end do
do t=1,mtapes
if (nflds(t) > 0) then
write(iulog,*)'Accumulation precision history file ', t, '=', nhstpr(t)
write(iulog,*)'Packing density history file ', t, '=', ndens(t)
write(iulog,*)'Number of time samples per file (MFILT) for history file ',t,' is ',mfilt(t)
end if
end do
end if
!
! set flag indicating h-tape contents are now defined (needed by addfld)
!
htapes_defined = .true.
!
! Now that masterlinkedlist is defined, construct primary and secondary hashing
! tables.
!
call bld_outfld_hash_tbls
()
call bld_htapefld_indices
()
return
end subroutine fldlst
!#######################################################################
subroutine inifld (t, listentry, avgflag, prec_acc, prec_wrt) 2,5
!
!-----------------------------------------------------------------------
!
! Purpose: Add a field to the active list for a history tape
!
! Method: Copy the data from the master field list to the active list for the tape
! Also: define mapping arrays from (col,chunk) -> (lon,lat)
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
!
! Arguments
!
integer, intent(in) :: t ! history tape index
type(master_entry), pointer :: listentry
character*1, intent(in) :: avgflag ! averaging flag
character*1, intent(in) :: prec_acc ! history buffer precision flag
character*1, intent(in) :: prec_wrt ! history output precision flag
!
! Local workspace
!
integer :: i ! index
integer :: n ! field index on defined tape
!
! Ensure that it is not to late to add a field to the history tape
!
if (htapes_defined) then
call endrun
('INIFLD: Attempt to add field '//listentry%field%name//' after history files set')
end if
nflds(t) = nflds(t) + 1
n = nflds(t)
!
! Copy field info.
!
if(n > size(tape(t)%hlist)) then
write(iulog,*) 'tape field miscount error ', n, size(tape(t)%hlist)
call endrun
()
end if
tape(t)%hlist(n)%field = listentry%field
!
! Set history buffer size and its output data type flags. Set them to
! the default values given by, respective, nhstpr(t) and ndens(t)
! if the input flags prec_acc and prec_wrt are blank; otherwise set to
! the specified values.
!
select case (prec_acc)
case (' ')
tape(t)%hlist(n)%hbuf_prec = nhstpr(t)
case ('4')
if (r4 /= r8) then
tape(t)%hlist(n)%hbuf_prec = 4
if (masterproc) then
write(iulog,*) 'INIFLD: History buffer for ', tape(t)%hlist(n)%field%name, &
' is real*4'
end if
else ! if kind values r4 and r8 are identical, ignore the request
tape(t)%hlist(n)%hbuf_prec = 8
if (masterproc) then
write(iulog,*) 'INIFLD: Requested change in history output size for ', &
tape(t)%hlist(n)%field%name, ' ignored'
write(iulog,*) ' because kind values r8 and r4 are identical'
end if
end if
case ('8')
tape(t)%hlist(n)%hbuf_prec = 8
if (masterproc) then
write(iulog,*) 'INIFLD: History buffer for ', tape(t)%hlist(n)%field%name, &
' is real*8'
end if
case default
call endrun
('INIFLD: unknown prec_acc='//prec_acc)
end select
select case (prec_wrt)
case (' ')
if (ndens(t) == 1) then
tape(t)%hlist(n)%hwrt_prec = 8
else
tape(t)%hlist(n)%hwrt_prec = 4
end if
case ('4')
tape(t)%hlist(n)%hwrt_prec = 4
if (masterproc) then
write(iulog,*) 'INIFLD: Output data type for ', tape(t)%hlist(n)%field%name, &
' is real*4'
end if
case ('8')
tape(t)%hlist(n)%hwrt_prec = 8
if (masterproc) then
write(iulog,*) 'INIFLD: Output data type for ', tape(t)%hlist(n)%field%name, &
' is real*8'
end if
case default
call endrun
('INIFLD: unknown prec_wrt='//prec_wrt)
end select
!
! Override the default averaging (masterlist) averaging flag if non-blank
!
if (avgflag == ' ') then
tape(t)%hlist(n)%avgflag = listentry%avgflag(t)
tape(t)%hlist(n)%time_op = listentry%time_op(t)
else
tape(t)%hlist(n)%avgflag = avgflag
select case (avgflag)
case ('A')
tape(t)%hlist(n)%time_op = 'mean'
case ('B')
tape(t)%hlist(n)%time_op = 'mean00z'
case ('I')
tape(t)%hlist(n)%time_op = ' '
case ('X')
tape(t)%hlist(n)%time_op = 'maximum'
case ('M')
tape(t)%hlist(n)%time_op = 'minimum'
case default
call endrun
('INIFLD: unknown avgflag='//avgflag)
end select
end if
#ifdef HDEBUG
write(iulog,*)'HDEBUG: ',__LINE__,' field ', tape(t)%hlist(n)%field%name, ' added as ', 'field number ', n,' on tape ', t
write(iulog,*)'units=',tape(t)%hlist(n)%field%units
write(iulog,*)'numlev=',tape(t)%hlist(n)%field%numlev
write(iulog,*)'avgflag=',tape(t)%hlist(n)%avgflag
write(iulog,*)'time_op=',tape(t)%hlist(n)%time_op
write(iulog,*)'hbuf_prec=',tape(t)%hlist(n)%hbuf_prec
write(iulog,*)'hwrt_prec=',tape(t)%hlist(n)%hwrt_prec
#endif
return
end subroutine inifld
subroutine column_init(t, nflds) 2,19
use dyn_grid
, only: get_dyn_grid_parm_real2d, get_dyn_grid_parm_real1d
use dycore
, only : dycore_is
integer, intent(in) :: t, nflds
integer :: ff, i
real(r8), allocatable :: lontmp (:) ! Temp array holding longitude values
real(r8), allocatable :: lontmp_st(:) ! Temp array holding longitude values
character(len=max_chars) :: lonlatname(pflds), lonname(pflds), latname(pflds) ! variable name
integer :: lonind (pflds,2) ! beginning and ending longitude range
integer :: latind (pflds,2) ! beginning and ending latitude range
integer :: lonind_st(pflds,2) ! beginning and ending staggered longitude range
integer :: latind_st(pflds,2) ! beginning and ending staggeredlatitude range
real(r8), pointer :: londeg(:,:), londeg_st(:,:), latdeg(:), latdeg_st(:)
character(len=max_chars) :: tmpname(pflds) ! variable name (will not be used)
integer :: group ! number of group
integer :: splon
plon = get_dyn_grid_parm
('plon')
splon = get_dyn_grid_parm
('splon')
plat = get_dyn_grid_parm
('plat')
londeg => get_dyn_grid_parm_real2d
('londeg')
latdeg => get_dyn_grid_parm_real1d
('latdeg')
if(dycore_is
('LR')) then
londeg_st => get_dyn_grid_parm_real2d
('londeg_st')
latdeg_st => get_dyn_grid_parm_real1d
('latdeg_st')
end if
!
! Setup column information if this field will be written as group
! First verify the column information in the namelist
!
ff = 1
allocate(lontmp(plon))
if(dycore_is
('LR')) then
allocate(lontmp_st(splon))
end if
do while (fincllonlat(ff,t) /= ' ')
lonlatname(ff) = trim(fincllonlat(ff,t))
call getlatind
(lonlatname(ff),latind(ff,1),latind(ff,2),latname(ff),latdeg ,plat)
do i = 1,plon
lontmp(i) = londeg(i,1)
if (lontmp(i) .lt. 0._r8) lontmp(i) = lontmp(i) + 360._r8
end do
call getlonind
(lonlatname(ff),lonind(ff,1),lonind(ff,2),lonname(ff),lontmp,plon)
#ifdef HDEBUG
write(iulog,*)'HDEBUG: ',__LINE__,' closest lon lat range for group ',lonlatname(ff),' is ',lonind(ff,:),latind(ff,:)
write(iulog,*) londeg(lonind(ff,1),1),londeg(lonind(ff,2),1),latdeg(latind(ff,1)),latdeg(latind(ff,2))
#endif
if(dycore_is
('LR')) then
call getlatind
(lonlatname(ff),latind_st(ff,1),latind_st(ff,2),tmpname(ff),latdeg_st ,plat-1)
do i = 1,splon
lontmp_st(i) = londeg_st(i,1)
if (lontmp_st(i) .lt. 0._r8) lontmp_st(i) = lontmp_st(i) + 360._r8
end do
call getlonind
(lonlatname(ff),lonind_st(ff,1),lonind_st(ff,2),tmpname(ff),lontmp_st,splon )
#ifdef HDEBUG
write(iulog,*)'HDEBUG: ',__LINE__,' closest staggered lon lat range for group ',lonlatname(ff),' is ',lonind_st(ff,:),latind_st(ff,:)
write(iulog,*) londeg_st(lonind_st(ff,1),1),londeg_st(lonind_st(ff,2),1),latdeg_st(latind_st(ff,1)),latdeg_st(latind_st(ff,2))
#endif
end if
ff = ff + 1
end do
deallocate(lontmp)
if(dycore_is
('LR')) then
deallocate(lontmp_st)
endif
group = ff-1
ngroup(t)=group
if (group .gt. 0) then
allocate (tape(t)%column (group))
if(dycore_is
('LR')) then
allocate (tape(t)%column_st(group))
endif
do i=1,nflds
allocate (tape(t)%hlist(i)%field_column_name(group))
end do
do ff = 1, group
tape(t)%column(ff)%lat_name=trim(latname(ff))
tape(t)%column(ff)%lon_name=trim(lonname(ff))
tape(t)%column(ff)%columnlon(:)=lonind(ff,:)
tape(t)%column(ff)%columnlat(:)=latind(ff,:)
tape(t)%column(ff)%num_lats = &
tape(t)%column(ff)%columnlat(2)-tape(t)%column(ff)%columnlat(1)+1
tape(t)%column(ff)%num_lons = &
tape(t)%column(ff)%columnlon(2)-tape(t)%column(ff)%columnlon(1)+1
do i=1,nflds
tape(t)%hlist(i)%field_column_name(ff) = &
trim(tape(t)%hlist(i)%field%name) // "_" // trim(lonname(ff)) // "_" // trim(latname(ff))
end do
if(dycore_is
('LR')) then
tape(t)%column_st(ff)%lat_name=trim(latname(ff)) // "_st"
tape(t)%column_st(ff)%lon_name=trim(lonname(ff)) // "_st"
tape(t)%column_st(ff)%columnlon(:)=lonind_st(ff,:)
tape(t)%column_st(ff)%columnlat(:)=latind_st(ff,:)
tape(t)%column_st(ff)%num_lats = &
tape(t)%column_st(ff)%columnlat(2)-tape(t)%column_st(ff)%columnlat(1)+1
tape(t)%column_st(ff)%num_lons = &
tape(t)%column_st(ff)%columnlon(2)-tape(t)%column_st(ff)%columnlon(1)+1
endif
end do
else
allocate (tape(t)%column(1))
do i=1,nflds
allocate (tape(t)%hlist(i)%field_column_name(1))
tape(t)%hlist(i)%field_column_name(1) = ' '
end do
tape(t)%column(1)%lat_name=' '
tape(t)%column(1)%lon_name=' '
tape(t)%column(1)%columnlon(:)=0
tape(t)%column(1)%columnlat(:)=0
tape(t)%column(1)%num_lats=0
tape(t)%column(1)%num_lons=0
end if
end subroutine column_init
!#######################################################################
subroutine strip_null(str) 5
character(len=*), intent(inout) :: str
do i=1,len(str)
if(ichar(str(i:i))==0) str(i:i)=' '
end do
end subroutine strip_null
character(len=max_fieldname_len) function strip_suffix (name) 5,2
!
!----------------------------------------------------------
!
! Purpose: Strip "&IC" suffix from fieldnames if it exists
!
!----------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: name
!
! Local workspace
!
integer :: n
!
!-----------------------------------------------------------------------
!
strip_suffix = ' '
do n = 1,fieldname_len
strip_suffix
(n:n) = name(n:n)
if(name(n+1:n+1 ) == ' ' ) return
if(name(n+1:n+fieldname_suffix_len) == fieldname_suffix) return
end do
strip_suffix
(fieldname_len+1:max_fieldname_len) = name(fieldname_len+1:max_fieldname_len)
return
end function strip_suffix
!#######################################################################
character(len=fieldname_len) function getname (inname) 4,4
!
!-----------------------------------------------------------------------
!
! Purpose: retrieve name portion of inname
!
! Method: If an averaging flag separater character is present (":") in inname,
! lop it off
!
!-------------------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: inname
!
! Local workspace
!
integer :: length
integer :: i
length = len (inname)
if (length < fieldname_len .or. length > fieldname_lenp2) then
write(iulog,*) 'GETNAME: bad length=',length
call endrun
end if
getname = ' '
do i=1,fieldname_len
if (inname(i:i) == ':') exit
getname
(i:i) = inname(i:i)
end do
return
end function getname
!#######################################################################
subroutine getlatind(inname,beglatind,endlatind,latname,latdeg,mlat) 2,4
!
!-----------------------------------------------------------------------
!
! Purpose: retrieve closest model lat index given north south latitude string
!
! Author: John Truesdale
!
!-------------------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: inname
character(len=*), intent(out) :: latname
integer, intent(out) :: beglatind,endlatind
integer, intent(in) :: mlat
real(r8), intent(in) :: latdeg(mlat)
!
! Local workspace
!
character(len=max_chars) str1,str2,tmpstr(4)
integer :: i,j,marker,latind(2),tmplen(4)
real(r8) :: latdegree(2)
!
!-------------------------------------------------------------------------------
!
str1=' '
str2=' '
tmpstr(:)= ' '
!
! make sure _ separator is present
!
if (scan(inname,'_').eq.0) then
write(iulog,*)'GETLATIND: Improperly formatted column string. Missing underscore character (xxxE_yyyS) ', &
inname,scan(inname,'_')
call endrun
end if
!
! split inname string into lat lon strings
!
marker=scan(inname,'_')
str1=inname(:marker-1)
str2=inname(marker+1:)
!
! split ranges of lats( or lons) into seperate substrings. Substrings 1,2 will contain lats(lons) from portion of string before underscore
! if a single column and not a range is specified substrings 1 and 2 will be the same
! and substrings 3,4 will contain lats(lons) from portion of main string after underscore character
! if a single column and not a range is specified substrings 3 and 4 will be the same
!
if (scan(str1,':') .ne. 0) then
marker=scan(str1,':')
tmpstr(1)=str1(:marker-1)
tmpstr(2)=str1(marker+1:)
else
tmpstr(1)=str1
tmpstr(2)=str1
end if
if (scan(str2,':') .ne. 0) then
marker=scan(str2,':')
tmpstr(3)=str2(:marker-1)
tmpstr(4)=str2(marker+1:)
else
tmpstr(3)=str2
tmpstr(4)=str2
end if
! check format of substrings - (number followed by single character north/south east/west designation)
do i = 1,4
tmplen(i)=len_trim(tmpstr(i))
if (verify(tmpstr(i),"0123456789.").ne.tmplen(i) .or. verify(tmpstr(i)(tmplen(i):tmplen(i)),"eEwWnNsS") .ne. 0) then
write(iulog,*)'GETLATIND (2): Improperly formatted column string. ',&
inname,verify(tmpstr(i),"0123456789."),tmplen(i),&
verify(tmpstr(i)(tmplen(i):tmplen(i)),"eEwWnNsS")
call endrun
end if
end do
! find latitude substrings and put into temporary work space tmpstr(1:2)
if (verify(tmpstr(1)(tmplen(1):tmplen(1)),"nNsSs").eq.0 .and. verify(tmpstr(2)(tmplen(2):tmplen(2)),"nNsSs").eq.0 ) then
else if (verify(tmpstr(3)(tmplen(3):tmplen(3)),"nNsSs").eq.0 .and. verify(tmpstr(3)(tmplen(3):tmplen(3)),"nNsSs").eq.0) then
tmpstr(1) = tmpstr(3)
tmplen(1)=tmplen(3)
tmpstr(2) = tmpstr(4)
tmplen(2)=tmplen(4)
else
call endrun
('GETLATIND (3): Improperly
end if
!
! convert lat substrings to real and if in southern hemisphere make negative
!
do i = 1,2
read(tmpstr(i)(1:tmplen(i)-1),*) latdegree(i)
if (verify(tmpstr(i)(tmplen(i):tmplen(i)),'sS').eq.0) then
latdegree(i)=(-1._r8)*latdegree(i)
end if
!
! Make sure specified latitudes is in bounds
!
if (latdegree(i) .lt. -90._r8 .or. latdegree(i) .gt. 90._r8) then
write(iulog,*)'GETLATIND: latitude for column namelist is out of range (-90 .. 90) value=',latdegree(i)
call endrun
endif
!
! Find closest lat index for each substring
!
latind(i)=mlat
do j = 1, mlat-1
if ( abs(latdeg(j)-latdegree(i)) .lt. &
abs(latdeg(j+1)-latdegree(i))) then
latind(i)=j
exit
endif
enddo
end do
!
! output begining and ending latitude indicies. If just a column is specified then beginning latitude index will be the same as the
! ending latitude index
!
if (latind(1) .le. latind(2) ) then
beglatind =latind(1)
endlatind =latind(2)
else
beglatind = latind(2)
endlatind = latind(1)
end if
if (beglatind .eq. endlatind) then
latname = 'LAT_'//trim(tmpstr(1))
else
if (latind(1) .le. latind(2) ) then
latname = 'LAT_'//trim(tmpstr(1)) // "_to_" // trim(tmpstr(2))
else
latname = 'LAT_'//trim(tmpstr(2)) // "_to_" // trim(tmpstr(1))
end if
end if
! one last sanity check
if(scan(latname,' ') > 0) then
latname(scan(latname,' '):) = ' '
end if
return
end subroutine getlatind
!#######################################################################
subroutine getlonind (inname,beglonind,endlonind,lonname,londeg,mlon) 2,4
!
!-----------------------------------------------------------------------
!
! Purpose: retrieve closes model lat index given north south latitude string
!
! Method: If an averaging flag separater character is present (":") in inname,
! lop it off
!
!-------------------------------------------------------------------------------
!
! Arguments
!
character(len=*) inname,lonname
integer :: beglonind,endlonind
integer :: mlon
real(r8) :: londeg(mlon)
!
! Local workspace
!
character(len=max_chars) str1,str2,tmpstr(4)
integer :: i,j,marker,lonind(2),tmplen(4)
real(r8) :: londegree(2)
real(r8) :: min, tmp
!
!-------------------------------------------------------------------------------
!
str1=' '
str2=' '
tmpstr(:)= ' '
!
! make sure _ separator is present
!
if (scan(inname,'_').eq.0) then
write(iulog,*)'GETLONIND: Improperly formatted column string. Missing underscore character (xxxE_yyyS) ',&
inname,scan(inname,'_')
call endrun
end if
!
! split string in to lat lon strings
!
marker=scan(inname,'_')
str1=inname(:marker-1)
str2=inname(marker+1:)
!
! split ranges of lats( or lons) into seperate substrings. Substrings 1,2 will contain lats(lons) from portion of string before underscore
! if a single column and not a range is specified substrings 1 and 2 will be the same
! and substrings 3,4 will contain lats(lons) from portion of main string after underscore character
! if a single column and not a range is specified substrings 3 and 4 will be the same
!
if (scan(str1,':') .ne. 0) then
marker=scan(str1,':')
tmpstr(1)=str1(:marker-1)
tmpstr(2)=str1(marker+1:)
else
tmpstr(1)=str1
tmpstr(2)=str1
end if
if (scan(str2,':') .ne. 0) then
marker=scan(str2,':')
tmpstr(3)=str2(:marker-1)
tmpstr(4)=str2(marker+1:)
else
tmpstr(3)=str2
tmpstr(4)=str2
end if
! check format of substrings - (number followed by single character north/south east/west designation)
do i = 1,4
tmplen(i)=len_trim(tmpstr(i))
if (verify(tmpstr(i),"0123456789.").ne.tmplen(i) .or. verify(tmpstr(i)(tmplen(i):tmplen(i)),"eEwWnNsS") .ne. 0) then
write(iulog,*)'GETLONIND (2): Improperly formatted column string. ', &
inname,verify(tmpstr(i),"0123456789."),tmplen(i), &
verify(tmpstr(i)(tmplen(i):tmplen(i)),"eEwWnNsS")
call endrun
end if
end do
! find longitude substrings and put into temporary work space tmpstr(1:2)
if (verify(tmpstr(1)(tmplen(1):tmplen(1)),"eEwW").eq.0 .and. verify(tmpstr(2)(tmplen(2):tmplen(2)),"eEwW").eq.0 ) then
else if (verify(tmpstr(3)(tmplen(3):tmplen(3)),"eEwW").eq.0 .and. verify(tmpstr(3)(tmplen(3):tmplen(3)),"eEwW").eq.0) then
tmpstr(1) = tmpstr(3)
tmplen(1)=tmplen(3)
tmpstr(2) = tmpstr(4)
tmplen(2)=tmplen(4)
else
call endrun
('GETLONIND (3): Improperly
end if
!
! convert lon substrings to real and make sure its degrees east
!
do i = 1,2
read(tmpstr(i)(1:tmplen(i)-1),*) londegree(i)
if (verify(tmpstr(i)(tmplen(i):tmplen(i)),'wW').eq.0) then
londegree(i) = 360._r8 - londegree(i)
end if
!
! Make sure specified longitudes are in bounds
!
if (londegree(i) .lt. 0._r8 .or. londegree(i) .gt. 360._r8) then
write(iulog,*)'GETLONIND: longitude for column namelist is out of range (0 .. 360) value=',londegree(i)
call endrun
endif
!
! Find closest lon index for each substring. If just a column is specified then beginning longitude index will be the same as the
! ending longitude index
!
min = 1.e+36_r8
do j = 1, mlon
tmp = abs(londeg(j)-londegree(i))
if ( tmp .le. min) then
min = tmp
lonind(i) = j
endif
end do
end do
!
! output begining and ending longitude indicies. If just a column is specified then beginning longitude index will be the same as the
! ending longitude index
!
if (lonind(1) .le. lonind(2) ) then
beglonind =lonind(1)
endlonind =lonind(2)
else
beglonind = lonind(2)
endlonind = lonind(1)
end if
if (beglonind .eq. endlonind) then
lonname = 'LON_'//trim(tmpstr(1))
else
if (lonind(1) .le. lonind(2) ) then
lonname = 'LON_'//trim(tmpstr(1)) // "_to_" // trim(tmpstr(2))
else
lonname = 'LON_'//trim(tmpstr(2)) // "_to_" // trim(tmpstr(1))
end if
end if
return
end subroutine getlonind
!#######################################################################
character(len=1) function getflag (inname) 2,2
!
!-----------------------------------------------------------------------
!
! Purpose: retrieve flag portion of inname
!
! Method: If an averaging flag separater character is present (":") in inname,
! return the character after it as the flag
!
!-------------------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: inname ! character string
!
! Local workspace
!
integer :: length ! length of inname
integer :: i ! loop index
length = len (inname)
if (length /= fieldname_lenp2) then
write(iulog,*) 'GETFLAG: bad length=',length
call endrun
end if
getflag = ' '
do i=1,fieldname_lenp2-1
if (inname(i:i) == ':') then
getflag = inname(i+1:i+1)
exit
end if
end do
return
end function getflag
!#######################################################################
subroutine list_index (list, name, index) 8
!
! Input arguments
!
character(len=*) , intent(in) :: list(pflds) ! input list of names, possibly ":" delimited
character(len=max_fieldname_len), intent(in) :: name ! name to be searched for
!
! Output arguments
!
integer, intent(out) :: index ! index of "name" in "list"
!
! Local workspace
!
character(len=fieldname_len) :: listname ! input name with ":" stripped off.
integer f ! field index
index = 0
do f=1,pflds
!
! Only list items
!
listname = getname (list(f))
if (listname == ' ') exit
if (listname == name) then
index = f
exit
end if
end do
return
end subroutine list_index
!#######################################################################
subroutine outfld (fname, field, idim, c) 986,8
!
!-----------------------------------------------------------------------
!
! Purpose: Accumulate (or take min, max, etc. as appropriate) input field
! into its history buffer for appropriate tapes
!
! Method: Check 'masterlist' whether the requested field 'fname' is active
! on one or more history tapes, and if so do the accumulation.
! If not found, return silently.
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: fname ! Field name--should be 8 chars long
integer, intent(in) :: idim ! Longitude dimension of field array
integer, intent(in) :: c ! chunk (physics) or latitude (dynamics) index
real(r8), intent(in) :: field(idim,*) ! Array containing field values
!
! Local variables
!
integer :: t, f ! tape, field indices
integer :: fl, fu ! upper, lower indices used in binary search thru sorted list
integer :: begdim1 ! on-node dim1 start index
integer :: enddim1 ! on-node dim1 end index
integer :: begdim2 ! on-node dim2 start index
integer :: enddim2 ! on-node dim2 end index
integer :: begdim3
integer :: enddim3
integer :: endi ! ending longitude index (reduced grid)
character*(max_fieldname_len) :: fname_loc ! max-char equivalent of fname
character*1 :: avgflag ! averaging flag
type (hbuffer_2d) :: hbuf ! history buffer
integer, pointer :: nacs(:) ! accumulation counter
type (dim_index_2d) :: dimind ! 2-D dimension index
logical :: flag_xyfill ! non-applicable xy points flagged with fillvalue
integer :: ff ! masterlist index pointer
integer :: ierr, ncol
!-----------------------------------------------------------------------
tape => history_tape
fname_loc = fname
ff = get_masterlist_indx
(fname_loc)
!
! If ( ff < 0 ), the field is not defined on the masterlist. This check
! is necessary because of coding errors calling outfld without first defining
! the field on masterlist.
!
if ( ff < 0 ) then
return
end if
!
! Next, check to see whether this field is active on one or more history
! tapes.
!
if ( .not. masterlist(ff)%thisentry%act_sometape ) then
return
end if
!
! Note, the field may be on any or all of the history files (primary
! and auxiliary).
!
! write(iulog,*)'fname_loc=',fname_loc
do 40 t=1,ptapes
if ( .not. masterlist(ff)%thisentry%actflag(t)) cycle
f = masterlist(ff)%thisentry%htapeindx(t)
!
! Update history buffer
!
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
avgflag = tape(t)%hlist(f)%avgflag
flag_xyfill = tape(t)%hlist(f)%field%flag_xyfill
nacs => tape(t)%hlist(f)%nacs(:,c)
call assoc_hbuf2d_with_hbuf3d
(hbuf, tape(t)%hlist(f)%hbuf, c)
if(associated(tape(t)%hlist(f)%field%colperdim3)) then
endi = tape(t)%hlist(f)%field%colperdim3(c)
else
endi = tape(t)%hlist(f)%field%enddim1 - tape(t)%hlist(f)%field%begdim1 + 1
end if
dimind = dim_index_2d (1,endi,begdim2,enddim2)
select case (avgflag)
case ('I') ! Instantaneous
call hbuf_accum_inst
(hbuf%buf4, hbuf%buf8, field, nacs, dimind, idim, flag_xyfill)
case ('A') ! Time average
call hbuf_accum_add
(hbuf%buf4, hbuf%buf8, field, nacs, dimind, idim, flag_xyfill)
case ('B') ! Time average only 00z values
call hbuf_accum_add00z
(hbuf%buf4, hbuf%buf8, field, nacs, dimind, idim, flag_xyfill)
case ('X') ! Maximum over time
call hbuf_accum_max
(hbuf%buf4, hbuf%buf8, field, nacs, dimind, idim, flag_xyfill)
case ('M') ! Minimum over time
call hbuf_accum_min
(hbuf%buf4, hbuf%buf8, field, nacs, dimind, idim, flag_xyfill)
case default
call endrun
('OUTFLD: invalid avgflag='//avgflag)
end select
40 continue
return
end subroutine outfld
!#######################################################################
logical function is_initfile (file_index) 12
!
!------------------------------------------------------------------------
!
! Purpose: to determine:
!
! a) if an IC file is active in this model run at all
! OR,
! b) if it is active, is the current file index referencing the IC file
! (IC file is always at ptapes)
!
!------------------------------------------------------------------------
!
! Arguments
!
integer, intent(in), optional :: file_index ! index of file in question
is_initfile = .false.
if (present(file_index)) then
if (inithist /= 'NONE' .and. file_index == ptapes) is_initfile = .true.
else
if (inithist /= 'NONE' ) is_initfile = .true.
end if
return
end function is_initfile
!#######################################################################
integer function strcmpf (name1, name2)
!
!-----------------------------------------------------------------------
!
! Purpose: Return the lexical difference between two strings
!
! Method: Use ichar() intrinsic as we loop through the names
!
!-----------------------------------------------------------------------
!
! Arguments
!
character(len=max_fieldname_len), intent(in) :: name1, name2 ! strings to compare
integer n ! loop index
do n=1,max_fieldname_len
strcmpf = ichar(name1(n:n)) - ichar(name2(n:n))
if (strcmpf /= 0) exit
end do
return
end function strcmpf
!#######################################################################
subroutine h_inquire (t) 2,2
use dycore
, only : dycore_is
!
!-----------------------------------------------------------------------
!
! Purpose: Ensure that the proper variables are on a history file
!
! Method: Issue the appropriate netcdf wrapper calls
!
!-----------------------------------------------------------------------
!
! Arguments
!
integer, intent(in) :: t ! tape index
!
! Local workspace
!
integer f,ff ! field index
integer ret ! return value from function call
integer londim ! longitude dimension id
integer latdim ! latitude dimension id
integer levdim ! level dimension id
integer ilevdim ! intfc dimension id
integer tbnddim ! time_bnds dimension id
integer old_mode ! returned from nf_set_fill
integer :: ierr
!
!
! Dimension id's
!
tape => history_tape
if(dycore_is
('UNSTRUCTURED') ) then
ierr=pio_inq_dimid (tape(t)%File, 'ncol', londim)
else
ierr=pio_inq_dimid (tape(t)%File, 'lat', latdim)
ierr=pio_inq_dimid (tape(t)%File, 'lon', londim)
end if
ierr=pio_inq_dimid (tape(t)%File, 'lev', levdim)
ierr=pio_inq_dimid (tape(t)%File, 'ilev', ilevdim)
ierr=pio_inq_dimid (tape(t)%File, 'tbnd', tbnddim)
!
! Create variables for model timing and header information
!
ierr=pio_inq_varid (tape(t)%File,'ndcur ', tape(t)%ndcurid)
ierr=pio_inq_varid (tape(t)%File,'nscur ', tape(t)%nscurid)
ierr=pio_inq_varid (tape(t)%File,'date ', tape(t)%dateid)
ierr=pio_inq_varid (tape(t)%File,'co2vmr ', tape(t)%co2vmrid)
ierr=pio_inq_varid (tape(t)%File,'ch4vmr ', tape(t)%ch4vmrid)
ierr=pio_inq_varid (tape(t)%File,'n2ovmr ', tape(t)%n2ovmrid)
ierr=pio_inq_varid (tape(t)%File,'f11vmr ', tape(t)%f11vmrid)
ierr=pio_inq_varid (tape(t)%File,'f12vmr ', tape(t)%f12vmrid)
ierr=pio_inq_varid (tape(t)%File,'sol_tsi ', tape(t)%sol_tsiid)
ierr=pio_inq_varid (tape(t)%File,'datesec ', tape(t)%datesecid)
ierr=pio_inq_varid (tape(t)%File,'nsteph ', tape(t)%nstephid)
ierr=pio_inq_varid (tape(t)%File,'time ', tape(t)%timeid)
ierr=pio_inq_varid (tape(t)%File,'time_bnds', tape(t)%tbndid)
ierr=pio_inq_varid (tape(t)%File,'date_written',tape(t)%date_writtenid)
ierr=pio_inq_varid (tape(t)%File,'time_written',tape(t)%time_writtenid)
#if ( defined BFB_CAM_SCAM_IOP )
ierr=pio_inq_varid (tape(t)%File,'tsec ',tape(t)%tsecid)
ierr=pio_inq_varid (tape(t)%File,'bdate ',tape(t)%bdateid)
#endif
!
! Obtain variable name from ID which was read from restart file
!
do f=1,nflds(t)
if(.not.associated(tape(t)%hlist(f)%varid)) then
allocate(tape(t)%hlist(f)%varid(max(1,ngroup(t))))
end if
!
! If this field will be put out as columns then get column names for field
!
if (ngroup(t) .gt. 0) then
do i=1,ngroup(t)
ierr=pio_inq_varid (tape(t)%File, tape(t)%hlist(f)%field_column_name(i), tape(t)%hlist(f)%varid(i))
ierr=pio_get_att(tape(t)%File, tape(t)%hlist(f)%varid(i), 'basename',tape(t)%hlist(f)%field%name)
end do
else
ierr=pio_inq_varid (tape(t)%File,tape(t)%hlist(f)%field%name, tape(t)%hlist(f)%varid(1))
end if
end do
if(masterproc) then
write(iulog,*)'H_INQUIRE: Successfully opened netcdf file '
end if
return
end subroutine h_inquire
!#######################################################################
subroutine add_default (name, tindex, flag) 460,5
!
!-----------------------------------------------------------------------
!
! Purpose: Add a field to the default "on" list for a given history file
!
! Method:
!
!-----------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: name ! field name
character(len=1), intent(in) :: flag ! averaging flag
integer, intent(in) :: tindex ! history tape index
!
! Local workspace
!
integer :: t ! file index
integer :: f ! field index
logical :: found ! flag indicates field found in masterlist
type(master_entry), pointer :: listentry
!
! Check validity of input arguments
!
if (tindex > ptapes) then
write(iulog,*)'ADD_DEFAULT: tape index=', tindex, ' is too big'
call endrun
end if
! Add to IC file if tindex = 0, reset to ptapes
if (tindex == 0) then
t = ptapes
if ( .not. is_initfile(file_index=t) ) return
else
t = tindex
end if
if (flag /= ' ' .and. flag /= 'A' .and. flag /= 'B' .and. flag /= 'I' .and. &
flag /= 'X' .and. flag /= 'M') then
call endrun
('ADD_DEFAULT: unknown averaging flag='//flag)
end if
!
! Look through master list for input field name. When found, set active
! flag for that tape to true. Also set averaging flag if told to use other
! than default.
!
found = .false.
listentry => get_entry_by_name
(masterlinkedlist, trim(name))
if(.not.associated(listentry)) then
call endrun
('ADD_DEFAULT: field='//name//' not found')
end if
listentry%actflag(t) = .true.
if (flag /= ' ') then
listentry%avgflag(t) = flag
select case (flag)
case ('A')
listentry%time_op(t) = 'mean'
case ('B')
listentry%time_op(t) = 'mean00z'
case ('I')
listentry%time_op(t) = ' '
case ('X')
listentry%time_op(t) = 'maximum'
case ('M')
listentry%time_op(t) = 'minimum'
case default
call endrun
('ADD_DEFAULT: unknown avgflag='//flag)
end select
end if
return
end subroutine add_default
!#######################################################################
subroutine h_override (t) 1,1
!
!-----------------------------------------------------------------------
!
! Purpose: Override default history tape contents for a specific tape
!
! Method: Copy the flag into the master field list
!
!-----------------------------------------------------------------------
!
! Arguments
!
integer, intent(in) :: t ! history tape index
!
! Local workspace
!
character(len=1) :: avgflg ! lcl equiv of avgflag_pertape(t) (to address xlf90 compiler bug)
type(master_entry), pointer :: listentry
avgflg = avgflag_pertape(t)
listentry=>masterlinkedlist
do while(associated(listentry))
select case (avgflg)
case ('A')
listentry%avgflag(t) = avgflag_pertape(t)
listentry%time_op(t) = 'mean'
case ('B')
listentry%avgflag(t) = avgflag_pertape(t)
listentry%time_op(t) = 'mean00z'
case ('I')
listentry%avgflag(t) = avgflag_pertape(t)
listentry%time_op(t) = ' '
case ('X')
listentry%avgflag(t) = avgflag_pertape(t)
listentry%time_op(t) = 'maximum'
case ('M')
listentry%avgflag(t) = avgflag_pertape(t)
listentry%time_op(t) = 'minimum'
case default
call endrun
('H_OVERRIDE: unknown avgflag='//avgflag_pertape(t))
end select
listentry=>listentry%next_entry
end do
end subroutine h_override
!#######################################################################
subroutine h_define (t, restart) 1,68
!
!-----------------------------------------------------------------------
!
! Purpose: Define contents of history file t
!
! Method: Issue the required netcdf wrapper calls to define the history file contents
!
!-----------------------------------------------------------------------
use pspect
, only : ptrn, ptrk, ptrm
use rgrid
, only : nlon, wnummax
use dyn_grid
, only : get_dyn_grid_parm_real2d, get_dyn_grid_parm_real1d
use dycore
, only : dycore_is
use time_manager
, only: get_step_size, get_ref_date, get_calendar
use filenames
, only: caseid
use string_utils
, only: to_upper
use abortutils
, only: endrun
use dycore
, only: dycore_is
use physconst
, only: pi
use spmd_utils
, only: iam, nsmps, npes
use cam_pio_utils
, only: cam_pio_createfile
!-----------------------------------------------------------------------
!
! Input arguments
!
integer, intent(in) :: t ! tape index
logical, intent(in) :: restart
!
! Local workspace
!
integer :: i, j ! longitude, latitude indices
integer :: k ! ISCCP vertical index
integer :: l ! ISCCP optical depth index
integer :: kl ! ISCCP merged k and l indices
integer :: f ! field index
integer :: ff ! varid index for fields output by column
integer :: numlev ! number of vertical levels (dimension and loop)
integer :: ncreal ! real data type for output
integer :: dtime ! timestep size
integer :: ndbase = 0 ! days component of base time
integer :: nsbase = 0 ! seconds component of base time
integer :: nbdate ! base date in yyyymmdd format
integer :: bdate ! base date in yyyymmdd format
integer :: nbsec ! time of day component of base date [seconds]
integer :: yr, mon, day ! year, month, day components of a date
integer :: slatdim ! staggered latitude dimension
integer :: slondim ! staggered longitude dimension
type(var_desc_t) :: slatvar ! variable id for staggered lat
type(var_desc_t) :: slonvar ! variable id for staggered lon
integer :: dimen4us(4) ! dimension array for staggered U winds
integer :: dimen4vs(4) ! dimension array for staggered V winds
type(var_desc_t) :: wsid ! Staggered latitude weight ID
real(r8), pointer:: londeg_st(:,:) ! Staggered grid point array (lon)
real(r8), pointer:: latdeg_st(: ) ! Staggered grid point array (lat)
real(r8), pointer:: w_staggered(:) ! Staggered location weights
real(r8) ailev(plevp) ! interface level values
real(r8), pointer :: latdeg(:) ! degrees gaussian latitudes
real(r8), pointer :: londeg(:,:) ! degrees longitude
real(r8), pointer :: w(:) ! grid weights
real(r8), parameter :: radtodeg = 180.0_r8/pi
real(r8), allocatable :: hlat(:), hlon(:), harea(:)
character(len=max_chars) str ! character temporary
character(len=max_fieldname_len) :: fname_tmp ! local copy of field name
character(len=max_chars) calendar ! Calendar type
!
! netcdf variables
!
integer ret ! function return value
integer :: timdim ! unlimited dimension id
type(var_desc_t) :: latvar ! latitude variable id
type(var_desc_t) :: lonvar ! longitude variable id
type(var_desc_t) :: areavar ! cell area variable id
type(var_desc_t), pointer :: glatvar(:) ! column latitude variable id
type(var_desc_t), pointer :: glonvar(:) ! column longitude variable id
type(var_desc_t) :: rlonvar ! reduced longitude variable id
type(var_desc_t) :: ps0var ! variable id for PS0
integer :: chardim ! character dimension id
real(r8) alon(plon) ! longitude values (degrees)
real(r8) alev(plev) ! level values (pascals)
real(r8) rlon(plon,plat) ! reduced longitudes (degrees)
real(r8) alat(plat) ! latitude values (degrees)
real(r8) prmid(npres) ! pressure midpoints of ISCCP data
real(r8) taumid(ntau) ! optical depth midpoints of ISCCP data
real(r8) prstau(npres*ntau)! prmid + taumid/1000
integer :: dimenchar(2) ! character dimension ids
integer :: dimen1(1) ! dimension ids (1d)
integer :: dimen2(2) ! dimension ids (2d)
integer :: dimen2t(2) ! dimension ids (2d time boundaries)
integer :: dimen3(3) ! dimension ids (3d)
integer :: dimen4f(4) ! dimension ids (4d at levels)
integer :: dimen4g(4) ! temp array holding dimension ids for groups of contiguous columns (4d)
integer :: dimen4i(4) ! dimension ids (4d at interfaces)
integer :: dimen4n(4) ! dimension ids (4d at isccp pressure levels)
integer :: nacsdims(2) ! dimension ids for nacs (used in restart file)
integer :: nacsdimcnt
integer londim ! longitude dimension id
integer latdim ! latitude dimension id
integer, allocatable:: grouplondim(:) ! longitude dimension id
integer, allocatable:: grouplatdim(:) ! latitude dimension id
integer levdim ! level dimension id
integer ilevdim ! interface dimension id
integer isccp_prs_dim ! dimension variable for ISCCP pressure levels
integer isccp_tau_dim ! dimension variable for ISCCP tau values
integer isccp_prstau_dim ! dimension variable for ISCCP pressure*tau levels
integer tbnddim ! time_bnds dimension id
type(var_desc_t):: levvar ! level variable id
type(var_desc_t):: ilevvar ! intfc variable id
type(var_desc_t) :: isccp_prs_var ! ISCCP mean pressure variable id
type(var_desc_t) :: isccp_tau_var ! ISCCP mean optical depth variable id
type(var_desc_t) :: isccp_prstau_var ! ISCCP mixed variable id
integer old_mode ! returned mode from netcdf call
type(var_desc_t) :: hyaiid ! hybrid A coef. intfc var id
type(var_desc_t) :: hybiid ! hybrid B coef. intfc var id
type(var_desc_t) :: hyamid ! hybrid A coef. level var id
type(var_desc_t) :: hybmid ! hybrid B coef. level var id
type(var_desc_t) :: ntrmid ! M truncation parameter var id
type(var_desc_t) :: ntrnid ! N truncation parameter var id
type(var_desc_t) :: ntrkid ! K truncation parameter var id
type(var_desc_t), pointer :: glatvar_st(:) ! column staggered latitude variable id
type(var_desc_t), pointer :: glonvar_st(:) ! column staggered longitude variable id
integer, allocatable :: grouplondim_st(:) ! staggered longitude dimension id
integer, allocatable :: grouplatdim_st(:) ! staggered latitude dimension id
integer :: splon
integer ndims
integer dim1s,dim2s ! global size of the first and second horizontal dim.
integer ncol
integer ncoldim ! horizontal dim for non rectangular grids
integer :: ierr
type(var_desc_t), pointer :: varid
integer, pointer :: ldof(:)
!
integer :: amode
if(restart) then
tape => restarthistory_tape
if(masterproc) write(iulog,*)'Opening netcdf history restart file ', trim(hrestpath(t))
else
tape => history_tape
if(masterproc) write(iulog,*)'Opening netcdf history file ', trim(nhfil(t))
end if
amode = PIO_CLOBBER
if(restart) then
call cam_pio_createfile
(tape(t)%File, hrestpath(t), amode)
else
call cam_pio_createfile
(tape(t)%File, nhfil(t), amode)
end if
!
! Setup netcdf file - create the dimensions of lat,lon,time,level
!
call get_horiz_grid_dim_d
(dim1s,dim2s)
if(dycore_is
('UNSTRUCTURED') ) then
ncol = dim1s*dim2s
ret = pio_def_dim (tape(t)%File, 'ncol', ncol, ncoldim)
ret = pio_put_att(tape(t)%File, PIO_GLOBAL, 'np', get_dyn_grid_parm
('np'))
ret = pio_put_att(tape(t)%File, PIO_GLOBAL, 'ne', get_dyn_grid_parm
('ne'))
else
ret = pio_def_dim (tape(t)%File, 'lat', dim2s, latdim)
ret = pio_def_dim (tape(t)%File, 'lon', dim1s, londim)
endif
if(dycore_is
('LR')) then
ret = pio_def_dim (tape(t)%File, 'slat', plat-1, slatdim)
splon = get_dyn_grid_parm
('splon')
ret = pio_def_dim (tape(t)%File, 'slon', splon, slondim)
end if
ret = pio_def_dim (tape(t)%File, 'lev', plev, levdim)
ret = pio_def_dim (tape(t)%File, 'ilev', plevp, ilevdim)
ret = pio_def_dim (tape(t)%File, 'isccp_prs', npres, isccp_prs_dim)
ret = pio_def_dim (tape(t)%File, 'isccp_tau', ntau, isccp_tau_dim)
ret = pio_def_dim (tape(t)%File, 'isccp_prstau', npres*ntau, isccp_prstau_dim)
ret = pio_def_dim (tape(t)%File, 'time', pio_unlimited, timdim)
ret = pio_def_dim (tape(t)%File, 'tbnd', 2, tbnddim)
ret = pio_def_dim (tape(t)%File, 'chars', 8, chardim)
!
! create dimensions for groups of contiguous columns
! variables with for single columns are created later in this routine
!
if (.not. restart .and. ngroup(t).ne.0) then
allocate(grouplatdim(ngroup(t)), grouplondim(ngroup(t)))
allocate(glatvar(ngroup(t)), glonvar(ngroup(t)))
do i = 1, ngroup(t)
if(scan(trim(tape(t)%column(i)%lat_name),' ')> 0) then
tape(t)%column(i)%lat_name(scan(tape(t)%column(i)%lat_name, ' '):)=' '
end if
if(scan(trim(tape(t)%column(i)%lon_name),' ')> 0) then
tape(t)%column(i)%lon_name(scan(tape(t)%column(i)%lon_name, ' '):)=' '
end if
ret = pio_def_dim (tape(t)%File, trim(tape(t)%column(i)%lat_name), tape(t)%column(i)%num_lats, grouplatdim(i))
ierr=pio_def_var (tape(t)%File,tape(t)%column(i)%lat_name,pio_double,grouplatdim(i:i),glatvar(i))
ierr=pio_put_att (tape(t)%File, glatvar(i),'long_name','latitude')
ierr=pio_put_att (tape(t)%File, glatvar(i),'units','degrees_north')
ret = pio_def_dim (tape(t)%File, trim(tape(t)%column(i)%lon_name), tape(t)%column(i)%num_lons, grouplondim(i))
ierr=pio_def_var (tape(t)%File,tape(t)%column(i)%lon_name,pio_double,grouplondim(i:i),glonvar(i))
ierr=pio_put_att (tape(t)%File, glonvar(i),'long_name','longitude')
ierr=pio_put_att (tape(t)%File, glonvar(i),'units','degrees_east')
end do
if(dycore_is
('LR')) then
!
! For staggered (FV) fields, include staggered dimension on h-file
!
do f=1,nflds(t)
fname_tmp = tape(t)%hlist(f)%field%name
if(fname_tmp .eq. 'US' .or. fname_tmp .eq. 'FU_S') then
if(.not.allocated(grouplatdim_st)) then
allocate(grouplatdim_st(ngroup(t)))
allocate(glatvar_st(ngroup(t)))
do i = 1, ngroup(t)
if(scan(trim(tape(t)%column_st(i)%lat_name),' ')> 0) then
tape(t)%column_st(i)%lat_name(scan(tape(t)%column_st(i)%lat_name, ' '):)=' '
end if
if(scan(trim(tape(t)%column_st(i)%lon_name),' ')> 0) then
tape(t)%column_st(i)%lon_name(scan(tape(t)%column_st(i)%lon_name, ' '):)=' '
end if
ret = pio_def_dim (tape(t)%File, tape(t)%column_st(i)%lat_name, &
tape(t)%column_st(i)%num_lats, grouplatdim_st(i))
ierr=pio_def_var (tape(t)%File,tape(t)%column_st(i)%lat_name,pio_double, &
grouplatdim_st(i:i),glatvar_st(i))
ierr=pio_put_att (tape(t)%File, glatvar_st(i),'long_name','staggered latitude')
ierr=pio_put_att (tape(t)%File, glatvar_st(i),'units','degrees_north')
end do
end if
else if(fname_tmp .eq. 'VS' .or. fname_tmp .eq. 'FV_S') then
if(.not.allocated(grouplondim_st)) then
allocate(grouplondim_st(ngroup(t)))
allocate(glonvar_st(ngroup(t)))
do i = 1, ngroup(t)
if(scan(trim(tape(t)%column_st(i)%lat_name),' ')> 0) then
tape(t)%column_st(i)%lat_name(scan(tape(t)%column_st(i)%lat_name, ' '):)=' '
end if
if(scan(trim(tape(t)%column_st(i)%lon_name),' ')> 0) then
tape(t)%column_st(i)%lon_name(scan(tape(t)%column_st(i)%lon_name, ' '):)=' '
end if
ret = pio_def_dim (tape(t)%File, tape(t)%column_st(i)%lon_name, &
tape(t)%column_st(i)%num_lons, grouplondim_st(i))
ierr=pio_def_var (tape(t)%File,tape(t)%column_st(i)%lon_name,pio_double, &
grouplondim_st(i:i),glonvar_st(i))
ierr=pio_put_att (tape(t)%File, glonvar_st(i),'long_name','staggered longitude')
ierr=pio_put_att (tape(t)%File, glonvar_st(i),'units','degrees_east')
end do
end if
end if
end do
endif
end if
!
! setup dimension arrays for 1,2,3,4d variables
!
if(dycore_is
('UNSTRUCTURED')) then
dimen1(1) = timdim
dimen2(1) = ncoldim
dimen2t(1) = tbnddim
dimen2t(2) = timdim
dimen3(1) = ncoldim
dimen3(2) = timdim
!
dimen4f(1) = ncoldim
dimen4f(2) = levdim
dimen4f(3) = timdim
dimen4i(1) = ncoldim
dimen4i(2) = ilevdim
dimen4i(3) = timdim
dimen4n(1) = ncoldim
dimen4n(2) = isccp_prstau_dim
dimen4n(3) = timdim
else
dimen1(1) = timdim
dimen2(1) = londim
dimen2(2) = latdim
dimen2t(1) = tbnddim
dimen2t(2) = timdim
dimen3(1) = londim
dimen3(2) = latdim
dimen3(3) = timdim
!
dimen4f(1) = londim
dimen4f(2) = latdim
dimen4f(3) = levdim
dimen4f(4) = timdim
dimen4i(1) = londim
dimen4i(2) = latdim
dimen4i(3) = ilevdim
dimen4i(4) = timdim
dimen4n(1) = londim
dimen4n(2) = latdim
dimen4n(3) = isccp_prstau_dim
dimen4n(4) = timdim
if(dycore_is
('LR')) then
dimen4us(1) = londim
dimen4us(2) = slatdim
dimen4us(3) = levdim
dimen4us(4) = timdim
dimen4vs(1) = slondim
dimen4vs(2) = latdim
dimen4vs(3) = levdim
dimen4vs(4) = timdim
endif
end if
! define variables to label the dimensions, use the same names as the
! dimensions
ierr=pio_def_var (tape(t)%File,'P0',pio_double,ps0var)
str = 'reference pressure'
ierr=pio_put_att (tape(t)%File, ps0var, 'long_name', str)
ierr=pio_put_att (tape(t)%File, ps0var, 'units', 'Pa')
if(dycore_is
('UNSTRUCTURED') ) then
ierr=pio_def_var (tape(t)%File,'lat',pio_double,(/NCOLDIM/),latvar)
ierr=pio_def_var (tape(t)%File,'area',pio_double,(/NCOLDIM/),areavar)
else
ierr=pio_def_var (tape(t)%File,'lat',pio_double,(/LATDIM/),latvar)
end if
ierr=pio_put_att (tape(t)%File, latvar, 'long_name', 'latitude')
ierr=pio_put_att (tape(t)%File, latvar, 'units', 'degrees_north')
if(dycore_is
('UNSTRUCTURED') ) then
ierr=pio_def_var (tape(t)%File,'lon',pio_double,(/NCOLDIM/),lonvar)
else
ierr=pio_def_var (tape(t)%File,'lon',pio_double,(/LONDIM/),lonvar)
end if
ierr=pio_put_att (tape(t)%File, lonvar,'long_name','longitude')
ierr=pio_put_att (tape(t)%File, lonvar,'units','degrees_east')
! If a staggered grid is in use, output the lat and lon arrays variables.
if(dycore_is
('LR')) then
ierr=pio_def_var (tape(t)%File, 'slat', pio_double,(/SLATDIM/),slatvar)
ierr=pio_put_att (tape(t)%File, slatvar, 'long_name', 'staggered latitude')
ierr=pio_put_att (tape(t)%File, slatvar, 'units', 'degrees_north')
ierr=pio_def_var (tape(t)%File,'slon',pio_double,(/SLONDIM/),slonvar)
ierr=pio_put_att (tape(t)%File, slonvar, 'long_name', 'staggered longitude')
ierr=pio_put_att(tape(t)%File, slonvar, 'units', 'degrees_east')
ierr=pio_def_var (tape(t)%File,'w_stag',pio_double,(/slatdim/),wsid)
str = 'staggered latitude weights'
ierr=pio_put_att (tape(t)%File, wsid, 'long_name', str)
endif
ierr=pio_def_var (tape(t)%File,'lev',pio_double,(/LEVDIM/),levvar)
str = 'hybrid level at midpoints (1000*(A+B))'
ierr=pio_put_att (tape(t)%File, levvar, 'long_name', str)
str = 'level'
ierr=pio_put_att (tape(t)%File, levvar, 'units', str)
ierr=pio_put_att (tape(t)%File, levvar, 'positive', 'down')
ierr=pio_put_att (tape(t)%File, levvar, 'standard_name', 'atmosphere_hybrid_sigma_pressure_coordinate')
ierr=pio_put_att (tape(t)%File, levvar, 'formula_terms', 'a: hyam b: hybm p0: P0 ps: PS')
ierr=pio_def_var (tape(t)%File,'ilev',pio_double,(/ILEVDIM/),ilevvar)
str = 'hybrid level at interfaces (1000*(A+B))'
ierr=pio_put_att (tape(t)%File, ilevvar, 'long_name', str)
str = 'level'
ierr=pio_put_att (tape(t)%File, ilevvar, 'units', str)
ierr=pio_put_att (tape(t)%File, ilevvar, 'positive', 'down')
ierr=pio_put_att (tape(t)%File, ilevvar, 'standard_name', 'atmosphere_hybrid_sigma_pressure_coordinate')
ierr=pio_put_att (tape(t)%File, ilevvar, 'formula_terms', 'a: hyai b: hybi p0: P0 ps: PS')
! ISCCP pressure, optical depth, and mixed dimension
ierr=pio_def_var (tape(t)%File, 'isccp_prs', PIO_DOUBLE, (/isccp_prs_dim/), isccp_prs_var)
str = 'Mean ISCCP pressure'
ierr=pio_put_att (tape(t)%File, isccp_prs_var, 'long_name', str)
str = 'mb'
ierr=pio_put_att (tape(t)%File, isccp_prs_var, 'units', str)
ierr=pio_put_att (tape(t)%File, isccp_prs_var, 'isccp_prs_bnds', prlim)
ierr=pio_def_var (tape(t)%File, 'isccp_tau', PIO_DOUBLE, (/isccp_tau_dim/), isccp_tau_var)
str = 'Mean ISCCP optical depth'
ierr=pio_put_att (tape(t)%File, isccp_tau_var, 'long_name', str)
str = 'unitless'
ierr=pio_put_att (tape(t)%File, isccp_tau_var, 'units', str)
ierr=pio_put_att (tape(t)%File, isccp_tau_var, 'isccp_tau_bnds', taulim)
ierr=pio_def_var (tape(t)%File, 'isccp_prstau', PIO_DOUBLE, (/isccp_prstau_dim/), isccp_prstau_var)
str = 'Mean pressure (mb).mean optical depth (unitless)/1000'
ierr=pio_put_att (tape(t)%File, isccp_prstau_var, 'long_name', str)
str = 'mixed'
ierr=pio_put_att (tape(t)%File, isccp_prstau_var, 'units', str)
call get_ref_date
(yr, mon, day, nbsec)
nbdate = yr*10000 + mon*100 + day
ierr=pio_def_var (tape(t)%File,'time',pio_double,(/TIMDIM/),tape(t)%timeid)
ierr=pio_put_att (tape(t)%File, tape(t)%timeid, 'long_name', 'time')
str = 'days since ' // date2yyyymmdd
(nbdate) // ' ' // sec2hms
(nbsec)
ierr=pio_put_att (tape(t)%File, tape(t)%timeid, 'units', str)
calendar = get_calendar
()
if ( trim(to_upper
(calendar)) == 'NO_LEAP' .or. trim(to_upper
(calendar)) == 'NOLEAP' ) then
ierr=pio_put_att (tape(t)%File, tape(t)%timeid, 'calendar', 'noleap')
else if ( trim(to_upper
(calendar)) == 'GREGORIAN' ) then
ierr=pio_put_att (tape(t)%File, tape(t)%timeid, 'calendar', 'gregorian')
else
call endrun
('H_DEFINE: unrecognized calendar type: '//trim(calendar) )
end if
ierr=pio_put_att (tape(t)%File, tape(t)%timeid, 'bounds', 'time_bnds')
ierr=pio_def_var (tape(t)%File,'time_bnds',pio_double,dimen2t,tape(t)%tbndid)
ierr=pio_put_att (tape(t)%File, tape(t)%tbndid, 'long_name', 'time interval endpoints')
!
! Character
!
dimenchar(1) = chardim
dimenchar(2) = timdim
ierr=pio_def_var (tape(t)%File,'date_written',PIO_CHAR,dimenchar, tape(t)%date_writtenid)
ierr=pio_def_var (tape(t)%File,'time_written',PIO_CHAR,dimenchar, tape(t)%time_writtenid)
!
! Integer Header
!
ierr=pio_def_var (tape(t)%File,'ntrm',PIO_INT,ntrmid)
str = 'spectral truncation parameter M'
ierr=pio_put_att (tape(t)%File, ntrmid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'ntrn',PIO_INT, ntrnid)
str = 'spectral truncation parameter N'
ierr=pio_put_att (tape(t)%File, ntrnid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'ntrk',PIO_INT,ntrkid)
str = 'spectral truncation parameter K'
ierr=pio_put_att (tape(t)%File, ntrkid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'ndbase',PIO_INT,tape(t)%ndbaseid)
str = 'base day'
ierr=pio_put_att (tape(t)%File, tape(t)%ndbaseid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'nsbase',PIO_INT,tape(t)%nsbaseid)
str = 'seconds of base day'
ierr=pio_put_att (tape(t)%File, tape(t)%nsbaseid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'nbdate',PIO_INT,tape(t)%nbdateid)
str = 'base date (YYYYMMDD)'
ierr=pio_put_att (tape(t)%File, tape(t)%nbdateid, 'long_name', str)
#if ( defined BFB_CAM_SCAM_IOP )
ierr=pio_def_var (tape(t)%File,'bdate',PIO_INT,tape(t)%bdateid)
str = 'base date (YYYYMMDD)'
ierr=pio_put_att (tape(t)%File, tape(t)%bdateid, 'long_name', str)
#endif
ierr=pio_def_var (tape(t)%File,'nbsec',PIO_INT,tape(t)%nbsecid)
str = 'seconds of base date'
ierr=pio_put_att (tape(t)%File, tape(t)%nbsecid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'mdt',PIO_INT,tape(t)%mdtid)
ierr=pio_put_att (tape(t)%File, tape(t)%mdtid, 'long_name', 'timestep')
ierr=pio_put_att (tape(t)%File, tape(t)%mdtid, 'units', 's')
if(.not. is_initfile
(file_index=t) .and. .not. dycore_is
('UNSTRUCTURED')) then
ierr=pio_def_var (tape(t)%File,'nlon',PIO_INT,(/latdim/),tape(t)%nlonid)
str = 'number of longitudes'
ierr=pio_put_att (tape(t)%File, tape(t)%nlonid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'wnummax',PIO_INT,(/latdim/),tape(t)%wnummaxid)
str = 'cutoff Fourier wavenumber'
ierr=pio_put_att (tape(t)%File, tape(t)%wnummaxid, 'long_name', str)
end if
!
! Floating point time-invariant
!
ierr=pio_def_var (tape(t)%File,'hyai',PIO_DOUBLE,(/ilevdim/),hyaiid)
str = 'hybrid A coefficient at layer interfaces'
ierr=pio_put_att (tape(t)%File, hyaiid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'hybi',PIO_DOUBLE,(/ilevdim/),hybiid)
str = 'hybrid B coefficient at layer interfaces'
ierr=pio_put_att (tape(t)%File, hybiid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'hyam',PIO_DOUBLE,(/levdim/),hyamid)
str = 'hybrid A coefficient at layer midpoints'
ierr=pio_put_att (tape(t)%File, hyamid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'hybm',PIO_DOUBLE,(/levdim/),hybmid)
str = 'hybrid B coefficient at layer midpoints'
ierr=pio_put_att (tape(t)%File, hybmid, 'long_name', str)
if(.not. dycore_is
('UNSTRUCTURED')) then
ierr=pio_def_var (tape(t)%File,'gw',PIO_DOUBLE,(/latdim/),gwid)
str = 'gauss weights'
ierr=pio_put_att (tape(t)%File, gwid, 'long_name', str)
end if
!
! Character header information
!
str = 'CF-1.0'
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'Conventions', str)
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'source', 'CAM')
#if ( defined BFB_CAM_SCAM_IOP )
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'CAM_GENERATED_FORCING','create SCAM IOP dataset')
#endif
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'case',caseid)
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'title',ctitle)
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'logname',logname)
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'host', host)
ierr= pio_put_att (tape(t)%File, PIO_GLOBAL, 'Version', &
'$Name$')
ierr= pio_put_att (tape(t)%File, PIO_GLOBAL, 'revision_Id', &
'$Id$')
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'initial_file', ncdata)
ierr=pio_put_att (tape(t)%File, PIO_GLOBAL, 'topography_file', bnd_topo)
!
! Create variables for model timing and header information
!
ierr=pio_def_var (tape(t)%File,'ndcur ',pio_int,dimen1,tape(t)%ndcurid)
str = 'current day (from base day)'
ierr=pio_put_att (tape(t)%File, tape(t)%ndcurid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'nscur ',pio_int,dimen1,tape(t)%nscurid)
str = 'current seconds of current day'
ierr=pio_put_att (tape(t)%File, tape(t)%nscurid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'date ',pio_int,dimen1,tape(t)%dateid)
str = 'current date (YYYYMMDD)'
ierr=pio_put_att (tape(t)%File, tape(t)%dateid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'co2vmr ',pio_double,dimen1,tape(t)%co2vmrid)
str = 'co2 volume mixing ratio'
ierr=pio_put_att (tape(t)%File, tape(t)%co2vmrid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'ch4vmr ',pio_double,dimen1,tape(t)%ch4vmrid)
str = 'ch4 volume mixing ratio'
ierr=pio_put_att (tape(t)%File, tape(t)%ch4vmrid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'n2ovmr ',pio_double,dimen1,tape(t)%n2ovmrid)
str = 'n2o volume mixing ratio'
ierr=pio_put_att (tape(t)%File, tape(t)%n2ovmrid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'f11vmr ',pio_double,dimen1,tape(t)%f11vmrid)
str = 'f11 volume mixing ratio'
ierr=pio_put_att (tape(t)%File, tape(t)%f11vmrid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'f12vmr ',pio_double,dimen1,tape(t)%f12vmrid)
str = 'f12 volume mixing ratio'
ierr=pio_put_att (tape(t)%File, tape(t)%f12vmrid, 'long_name', str)
ierr=pio_def_var (tape(t)%File,'sol_tsi ',pio_double,dimen1,tape(t)%sol_tsiid)
str = 'total solar irradiance'
ierr=pio_put_att (tape(t)%File, tape(t)%sol_tsiid, 'long_name', str)
str = 'W/m2'
ierr=pio_put_att (tape(t)%File, tape(t)%sol_tsiid, 'units', str)
ierr=pio_def_var (tape(t)%File,'datesec ',pio_int,dimen1, tape(t)%datesecid)
str = 'current seconds of current date'
ierr=pio_put_att (tape(t)%File, tape(t)%datesecid, 'long_name', str)
#if ( defined BFB_CAM_SCAM_IOP )
ierr=pio_def_var (tape(t)%File,'tsec ',pio_int,dimen1, tape(t)%tsecid)
str = 'current seconds of current date needed for scam'
ierr=pio_put_att (tape(t)%File, tape(t)%tsecid, 'long_name', str)
#endif
ierr=pio_def_var (tape(t)%File,'nsteph ',pio_int,dimen1,tape(t)%nstephid)
str = 'current timestep'
ierr=pio_put_att (tape(t)%File, tape(t)%nstephid, 'long_name', str)
!
! Create variables and attributes for field list
!
do f=1,nflds(t)
select case (tape(t)%hlist(f)%avgflag)
case ('A')
tape(t)%hlist(f)%time_op(:) = 'mean'
case ('B')
tape(t)%hlist(f)%time_op(:) = 'mean00z'
case ('I')
tape(t)%hlist(f)%time_op(:) = ' '
case ('X')
tape(t)%hlist(f)%time_op(:) = 'maximum'
case ('M')
tape(t)%hlist(f)%time_op(:) = 'minimum'
case default
call endrun
('h_define: unknown avgflag='//tape(t)%hlist(f)%avgflag)
end select
numlev = tape(t)%hlist(f)%field%numlev
if (tape(t)%hlist(f)%hwrt_prec == 8) then
ncreal = pio_double
else
ncreal = pio_real
end if
if(.not.associated(tape(t)%hlist(f)%varid)) then
allocate(tape(t)%hlist(f)%varid(1:max(1,ngroup(t))))
end if
fname_tmp = strip_suffix
(tape(t)%hlist(f)%field%name)
nacsdimcnt=0
if (tape(t)%hlist(f)%field%flag_xyfill) then
if(dycore_is
('UNSTRUCTURED')) then
nacsdims(1) = ncoldim
nacsdimcnt=1
else
nacsdims= dimen2
nacsdimcnt=2
end if
if(dycore_is
('LR') .and. numlev==plev) then
if(fname_tmp .eq. 'US' .or. fname_tmp .eq. 'FU_S') then
nacsdims= dimen4us(1:2)
else if(fname_tmp .eq. 'VS' .or. fname_tmp .eq. 'FV_S') then
nacsdims= dimen4vs(1:2)
else
nacsdims= dimen4f(1:2)
end if
end if
end if
!
! Create variables and atributes for fields written out as columns
!
if (.not. restart .and. ngroup(t).ne.0) then
do i=1,ngroup(t)
ff=(f-1)*ngroup(t)+i
dimen4g(1)=grouplondim(i)
dimen4g(2)=grouplatdim(i)
dimen4g(4)=timdim
if(dycore_is
('LR')) then
if(fname_tmp .eq. 'US' .or. fname_tmp .eq. 'FU_S') then
dimen4g(2)=grouplatdim_st(i)
else if(fname_tmp .eq. 'VS' .or. fname_tmp .eq. 'FV_S') then
dimen4g(1)=grouplondim_st(i)
end if
endif
varid => tape(t)%hlist(f)%varid(i)
if (numlev == npres*ntau .and. tape(t)%hlist(f)%field%flag_isccplev) then
dimen4g(3)=isccp_prstau_dim
ierr=pio_def_var(tape(t)%File, tape(t)%hlist(f)%field_column_name(i),ncreal,&
dimen4g,varid)
else if (numlev == 1) then
dimen4g(3)=timdim
ierr=pio_def_var(tape(t)%File,trim(tape(t)%hlist(f)%field_column_name(i)),&
ncreal,dimen4g(1:3),varid)
else if (numlev == plev) then
dimen4g(3)=levdim
ierr=pio_def_var(tape(t)%File, trim(tape(t)%hlist(f)%field_column_name(i)),&
ncreal,dimen4g,varid)
else if (numlev == plevp) then
dimen4g(3)=ilevdim
ierr=pio_def_var(tape(t)%File, trim(tape(t)%hlist(f)%field_column_name(i)),&
ncreal,dimen4g,varid)
else
write(iulog,*)'H_DEFINE: bad numlev=',numlev
call endrun
end if
ierr= pio_put_att(tape(t)%File, varid, 'basename',tape(t)%hlist(f)%field%name)
str = tape(t)%hlist(f)%field%sampling_seq
if (len_trim(str)>0) then
ierr=pio_put_att (tape(t)%File, varid, &
'Sampling_Sequence', str)
end if
if (tape(t)%hlist(f)%field%flag_xyfill) then
! Add both _FillValue and missing_value to cover expectations of various applications.
! The attribute type must match the data type.
if (tape(t)%hlist(f)%hwrt_prec == 8) then
ierr=pio_put_att (tape(t)%File, varid, '_FillValue', fillvalue)
ierr=pio_put_att (tape(t)%File, varid, 'missing_value', fillvalue)
else
ierr=pio_put_att (tape(t)%File, varid, '_FillValue', fillvalue_r4)
ierr=pio_put_att (tape(t)%File, varid, 'missing_value', fillvalue_r4)
end if
end if
str = tape(t)%hlist(f)%field%units
ierr=pio_put_att (tape(t)%File, varid, 'units', str)
str = tape(t)%hlist(f)%field%long_name
ierr=pio_put_att (tape(t)%File, varid, 'long_name', str)
!
! Assign field attributes defining valid levels and averaging info
!
str = tape(t)%hlist(f)%time_op
select case (str)
case ('mean', 'maximum', 'minimum' )
ierr=pio_put_att (tape(t)%File, varid,'cell_methods', 'time: '//str)
end select
end do
!
! else create variables and atributes for fields written out as a full model grid
!
else
!
! If an IC field, strip "&IC" from name
!
if(.not.associated(tape(t)%hlist(f)%varid)) then
allocate(tape(t)%hlist(f)%varid(1))
end if
varid => tape(t)%hlist(f)%varid(1)
if (numlev == npres*ntau .and. tape(t)%hlist(f)%field%flag_isccplev) then
if(dycore_is
('UNSTRUCTURED')) then
ndims=3
else
ndims=4
end if
if(restart) ndims = ndims-1 ! removes the time dim
ierr=pio_def_var(tape(t)%File, fname_tmp, ncreal,dimen4n(1:ndims),varid)
else if (numlev == 1) then
if(dycore_is
('UNSTRUCTURED')) then
ndims=2
else
ndims=3
end if
if(restart) ndims = ndims-1 ! removes the time dim
ierr=pio_def_var(tape(t)%File,fname_tmp,ncreal,dimen3(1:ndims),varid)
else if (numlev == plev) then
if(dycore_is
('UNSTRUCTURED')) then
ndims=3
else
ndims=4
end if
if(restart) ndims = ndims-1 ! removes the time dim
if(dycore_is
('LR')) then
if(fname_tmp .eq. 'US' .or. fname_tmp .eq. 'FU_S') then
ierr=pio_def_var(tape(t)%File,fname_tmp,ncreal,dimen4us,varid)
else if(fname_tmp .eq. 'VS' .or. fname_tmp .eq. 'FV_S') then
ierr=pio_def_var(tape(t)%File,fname_tmp,ncreal,dimen4vs,varid)
else
ierr=pio_def_var(tape(t)%File,fname_tmp,ncreal,dimen4f,varid)
end if
else
ierr=pio_def_var(tape(t)%File, fname_tmp, ncreal,dimen4f(1:ndims),varid)
end if
else if (numlev == plevp) then
if(dycore_is
('UNSTRUCTURED')) then
ndims=3
else
ndims=4
end if
if(restart) ndims = ndims-1 ! removes the time dim
ierr=pio_def_var(tape(t)%File, fname_tmp, ncreal,dimen4i(1:ndims),varid)
else
write(iulog,*)'H_DEFINE: bad numlev=',numlev
call endrun
end if
!
str = tape(t)%hlist(f)%field%sampling_seq
if (len_trim(str) > 0) then
ierr=pio_put_att (tape(t)%File, varid, 'Sampling_Sequence', str)
end if
if (tape(t)%hlist(f)%field%flag_xyfill) then
if (tape(t)%hlist(f)%hwrt_prec == 8) then
ierr=pio_put_att (tape(t)%File, varid, '_FillValue', fillvalue)
ierr=pio_put_att (tape(t)%File, varid, 'missing_value', fillvalue)
else
ierr=pio_put_att (tape(t)%File, varid, '_FillValue', fillvalue_r4)
ierr=pio_put_att (tape(t)%File, varid, 'missing_value', fillvalue_r4)
end if
end if
ierr=pio_put_att (tape(t)%File, varid, 'units', tape(t)%hlist(f)%field%units)
str = tape(t)%hlist(f)%field%long_name
ierr=pio_put_att (tape(t)%File, varid, 'long_name', str)
!
! Assign field attributes defining valid levels and averaging info
!
str = tape(t)%hlist(f)%time_op
select case (str)
case ('mean', 'maximum', 'minimum' )
ierr=pio_put_att (tape(t)%File, varid,'cell_methods', 'time: '//str)
end select
if(restart) then
if( nacsdimcnt>0) then
ierr=pio_def_var(tape(t)%File,trim(fname_tmp)//'_nacs',pio_int,nacsdims(1:nacsdimcnt),&
tape(t)%hlist(f)%nacs_varid)
else
ierr=pio_def_var(tape(t)%File,trim(fname_tmp)//'_nacs',pio_int,&
tape(t)%hlist(f)%nacs_varid)
end if
endif
endif
end do
!
ret = pio_enddef(tape(t)%File)
if(masterproc) then
write(iulog,*)'H_DEFINE: Successfully opened netcdf file '
endif
!
! Write time-invariant portion of history header
!
ierr = pio_put_var (tape(t)%File, ps0var, (/ps0/))
londeg => get_dyn_grid_parm_real2d
('londeg')
latdeg => get_dyn_grid_parm_real1d
('latdeg')
if(dycore_is
('LR')) then
londeg_st => get_dyn_grid_parm_real2d
('londeg_st')
latdeg_st => get_dyn_grid_parm_real1d
('latdeg_st')
end if
if(dycore_is
('UNSTRUCTURED')) then
! save memory for these global arrays by doing them 1 by 1:
allocate(harea(ncol))
call get_horiz_grid_d
(ncol, clat_d_out=harea)
do j=1,ncol
harea(j) = harea(j)*radtodeg
end do
ierr = pio_put_var (tape(t)%File, latvar, harea)
call get_horiz_grid_d
(ncol, clon_d_out=harea)
do j=1,ncol
harea(j) = harea(j)*radtodeg
end do
ierr = pio_put_var (tape(t)%File, lonvar, harea)
call get_horiz_grid_d
(ncol, area_d_out=harea)
ierr = pio_put_var (tape(t)%File, areavar, harea)
deallocate(harea)
else
latdeg => get_dyn_grid_parm_real1d
('latdeg')
ierr=pio_put_var (tape(t)%File, latvar, latdeg)
do i=1,plon
alon(i) = (i-1) * 360.0_r8 / plon
end do
if (single_column) alon(1)=scmlon
ierr = pio_put_var (tape(t)%File, lonvar, alon)
end if
if(dycore_is
('LR')) then
latdeg_st => get_dyn_grid_parm_real1d
('latdeg_st')
ierr = pio_put_var (tape(t)%File, slatvar, latdeg_st)
londeg_st => get_dyn_grid_parm_real2d
('londeg_st')
ierr = pio_put_var (tape(t)%File, slonvar, londeg_st(:,1))
w_staggered => get_dyn_grid_parm_real1d
('w_staggered')
ierr = pio_put_var (tape(t)%File, wsid, w_staggered)
endif
!
! write out coordinate variables for columns
!
if (.not. restart .and. ngroup(t).ne.0) then
do i = 1, ngroup(t)
ierr = pio_put_var (tape(t)%File, glonvar(i), &
londeg(tape(t)%column(i)%columnlon(1):tape(t)%column(i)%columnlon(2),1))
ierr = pio_put_var (tape(t)%File, glatvar(i), &
latdeg(tape(t)%column(i)%columnlat(1):tape(t)%column(i)%columnlat(2)))
end do
deallocate(glonvar, glatvar,grouplatdim,grouplondim)
!
! For staggered (FV) fields, write out staggered coordinate variables for columns
!
do f=1,nflds(t)
select case (tape(t)%hlist(f)%field%name)
case ('US','FU_S')
do i = 1, ngroup(t)
ierr = pio_put_var(tape(t)%File, glatvar_st(i), &
latdeg_st(tape(t)%column_st(i)%columnlat(1):tape(t)%column_st(i)%columnlat(2)))
end do
case ('VS','FV_S')
do i = 1, ngroup(t)
ierr = pio_put_var(tape(t)%File, glonvar_st(i), &
londeg_st(tape(t)%column_st(i)%columnlon(1):tape(t)%column_st(i)%columnlon(2),1))
end do
end select
end do
end if
if(allocated(grouplondim_st)) then
deallocate(glonvar_st, grouplondim_st)
end if
if(allocated(grouplatdim_st)) then
deallocate(glatvar_st, grouplatdim_st)
end if
!
! 0.01 converts Pascals to millibars
!
alev(:plev) = 0.01_r8*ps0*(hyam(:plev) + hybm(:plev))
ailev(:plevp) = 0.01_r8*ps0*(hyai(:plevp) + hybi(:plevp))
do k=1,npres
prmid(k) = 0.5_r8*(prlim(k) + prlim(k+1))
end do
do k=1,ntau
taumid(k) = 0.5_r8*(taulim(k) + taulim(k+1))
end do
!JR Kludgey way of combining pressure and optical depth into a single dimension:
!JR pressure in millibars will show up on the left side of the decimal point,
!JR optical depth/1000 will show up on the right
do k=1,npres
do l=1,ntau
kl = (k-1)*ntau + l
prstau(kl) = prmid(k) + taumid(l)*0.001_r8
end do
end do
ierr = pio_put_var (tape(t)%File, levvar, alev)
ierr = pio_put_var (tape(t)%File, ilevvar, ailev)
ierr = pio_put_var (tape(t)%File, hyaiid, hyai)
ierr = pio_put_var (tape(t)%File, hybiid, hybi)
ierr = pio_put_var (tape(t)%File, hyamid, hyam)
ierr = pio_put_var (tape(t)%File, hybmid, hybm)
if(.not. dycore_is
('UNSTRUCTURED')) then
w => get_dyn_grid_parm_real1d
('w')
ierr = pio_put_var (tape(t)%File, gwid, w)
end if
ierr = pio_put_var (tape(t)%File, isccp_prs_var, prmid)
ierr = pio_put_var (tape(t)%File, isccp_tau_var, taumid)
ierr = pio_put_var (tape(t)%File, isccp_prstau_var, prstau)
ierr = pio_put_var (tape(t)%File, ntrmid, (/ptrm/))
ierr = pio_put_var (tape(t)%File, ntrnid, (/ptrn/))
ierr = pio_put_var (tape(t)%File, ntrkid, (/ptrk/))
dtime = get_step_size
()
ierr = pio_put_var (tape(t)%File, tape(t)%mdtid, (/dtime/))
!
! Model date info
!
ierr = pio_put_var (tape(t)%File, tape(t)%ndbaseid, (/ndbase/))
ierr = pio_put_var (tape(t)%File, tape(t)%nsbaseid, (/nsbase/))
ierr = pio_put_var (tape(t)%File, tape(t)%nbdateid, (/nbdate/))
#if ( defined BFB_CAM_SCAM_IOP )
ierr = pio_put_var (tape(t)%File, tape(t)%bdateid, (/nbdate/))
#endif
ierr = pio_put_var (tape(t)%File, tape(t)%nbsecid, (/nbsec/))
!
! Reduced grid info
!
if(.not. is_initfile
(file_index=t) .and. .not. dycore_is
('UNSTRUCTURED')) then
ierr = pio_put_var (tape(t)%File, tape(t)%nlonid, nlon)
ierr = pio_put_var (tape(t)%File, tape(t)%wnummaxid, wnummax)
end if
end subroutine h_define
!#######################################################################
character(len=10) function date2yyyymmdd (date) 1,1
! Input arguments
integer, intent(in) :: date
! Local workspace
integer :: year ! year of yyyy-mm-dd
integer :: month ! month of yyyy-mm-dd
integer :: day ! day of yyyy-mm-dd
if (date < 0) then
call endrun
('DATE2YYYYMMDD: negative date not allowed')
end if
year = date / 10000
month = (date - year*10000) / 100
day = date - year*10000 - month*100
write(date2yyyymmdd,80) year, month, day
80 format(i4.4,'-',i2.2,'-',i2.2)
return
end function date2yyyymmdd
!#######################################################################
character(len=8) function sec2hms (seconds) 1,3
! Input arguments
integer, intent(in) :: seconds
! Local workspace
integer :: hours ! hours of hh:mm:ss
integer :: minutes ! minutes of hh:mm:ss
integer :: secs ! seconds of hh:mm:ss
if (seconds < 0 .or. seconds > 86400) then
write(iulog,*)'SEC2HRS: bad input seconds:', seconds
call endrun
()
end if
hours = seconds / 3600
minutes = (seconds - hours*3600) / 60
secs = (seconds - hours*3600 - minutes*60)
if (minutes < 0 .or. minutes > 60) then
write(iulog,*)'SEC2HRS: bad minutes = ',minutes
call endrun
()
end if
if (secs < 0 .or. secs > 60) then
write(iulog,*)'SEC2HRS: bad secs = ',secs
call endrun
()
end if
write(sec2hms,80) hours, minutes, secs
80 format(i2.2,':',i2.2,':',i2.2)
return
end function sec2hms
!#######################################################################
subroutine h_normalize (f, t) 1,1
use dycore
, only: dycore_is
!
!-----------------------------------------------------------------------
!
! Purpose: Normalize fields on a history file by the number of accumulations
!
! Method: Loop over fields on the tape. Need averaging flag and number of
! accumulations to perform normalization.
!
!-----------------------------------------------------------------------
!
! Input arguments
!
integer, intent(in) :: f ! field index
integer, intent(in) :: t ! tape index
!
! Local workspace
!
integer begdim2 ! on-node vert start index
integer enddim2 ! on-node vert end index
integer c ! chunk (or lat) index
integer endi ! terminating column index
integer begdim1 ! on-node dim1 start index
integer enddim1 ! on-node dim1 end index
integer begdim3 ! on-node chunk or lat start index
integer enddim3 ! on-node chunk or lat end index
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer k
logical :: flag_xyfill ! non-applicable xy points flagged with fillvalue
character*1 avgflag ! averaging flag
call t_startf ('h_normalize')
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
avgflag = tape(t)%hlist(f)%avgflag
!
! normalize by number of accumulations for averaged case
!
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
flag_xyfill = tape(t)%hlist(f)%field%flag_xyfill
do c=begdim3,enddim3
! Some slices might not be entirely full -- use endi instead of enddim2
if(associated(tape(t)%hlist(f)%field%colperdim3)) then
endi = tape(t)%hlist(f)%field%colperdim3(c)
else
endi = enddim1-begdim1+1
end if
ib = begdim1
ie = begdim1+endi-1
jb = begdim2
je = enddim2
if (flag_xyfill) then
if (associated(tape(t)%hlist(f)%hbuf%buf8)) then
do k=jb,je
where (tape(t)%hlist(f)%nacs(ib:ie,c) == 0)
tape(t)%hlist(f)%hbuf%buf8(ib:ie,k,c) = fillvalue
endwhere
end do
else if (associated(tape(t)%hlist(f)%hbuf%buf4)) then
do k=jb,je
where (tape(t)%hlist(f)%nacs(ib:ie,c) == 0)
tape(t)%hlist(f)%hbuf%buf4(ib:ie,k,c) = fillvalue
endwhere
end do
end if
end if
if (avgflag == 'A' .or. avgflag == 'B') then
if (associated(tape(t)%hlist(f)%hbuf%buf8)) then
if(flag_xyfill) then
do k=jb,je
where (tape(t)%hlist(f)%nacs(ib:ie,c) /= 0)
tape(t)%hlist(f)%hbuf%buf8(ib:ie,k,c) = &
tape(t)%hlist(f)%hbuf%buf8(ib:ie,k,c) &
/ tape(t)%hlist(f)%nacs(ib:ie,c)
endwhere
end do
else if(tape(t)%hlist(f)%nacs(1,c) > 0) then
do k=jb,je
tape(t)%hlist(f)%hbuf%buf8(ib:ie,k,c) = &
tape(t)%hlist(f)%hbuf%buf8(ib:ie,k,c) &
/ tape(t)%hlist(f)%nacs(1,c)
end do
end if
else if (associated(tape(t)%hlist(f)%hbuf%buf4)) then
if(flag_xyfill) then
do k=jb,je
where (tape(t)%hlist(f)%nacs(ib:ie,c) /= 0)
tape(t)%hlist(f)%hbuf%buf4(ib:ie,k,c) = &
tape(t)%hlist(f)%hbuf%buf4(ib:ie,k,c) &
/ tape(t)%hlist(f)%nacs(ib:ie,c)
endwhere
end do
else if(tape(t)%hlist(f)%nacs(1,c) > 0) then
do k=jb,je
tape(t)%hlist(f)%hbuf%buf4(ib:ie,k,c) = &
tape(t)%hlist(f)%hbuf%buf4(ib:ie,k,c) &
/ tape(t)%hlist(f)%nacs(1,c)
end do
end if
end if
end if
end do
call t_stopf ('h_normalize')
return
end subroutine h_normalize
!#######################################################################
subroutine h_zero (f, t) 2,3
use dycore
, only: dycore_is
!
!-----------------------------------------------------------------------
!
! Purpose: Zero out accumulation buffers for a tape
!
! Method: Loop through fields on the tape
!
!-----------------------------------------------------------------------
!
integer, intent(in) :: f ! field index
integer, intent(in) :: t ! tape index
!
! Local workspace
!
integer begdim1 ! on-node 1st dim start index
integer enddim1 ! on-node 1st dim end index
integer begdim2 ! on-node vert start index
integer enddim2 ! on-node vert end index
integer c ! chunk index
integer endi ! terminating column index
integer begdim3 ! on-node chunk or lat start index
integer enddim3 ! on-node chunk or lat end index
type (dim_index_3d) :: dimind ! 3-D dimension index
call t_startf ('h_zero')
begdim1 = tape(t)%hlist(f)%field%begdim1
enddim1 = tape(t)%hlist(f)%field%enddim1
begdim2 = tape(t)%hlist(f)%field%begdim2
enddim2 = tape(t)%hlist(f)%field%enddim2
begdim3 = tape(t)%hlist(f)%field%begdim3
enddim3 = tape(t)%hlist(f)%field%enddim3
if(associated(tape(t)%hlist(f)%field%colperdim3)) then
do c=begdim3,enddim3
endi = tape(t)%hlist(f)%field%colperdim3(c)
dimind = dim_index_3d (begdim1,begdim1+endi-1,begdim2,enddim2,c,c)
call set_hbuf_section_to_val
(tape(t)%hlist(f)%hbuf,dimind,0._r8)
tape(t)%hlist(f)%nacs(:,:) = 0
end do
else
endi = tape(t)%hlist(f)%field%enddim1 - tape(t)%hlist(f)%field%begdim1 + 1
dimind = dim_index_3d (begdim1,begdim1+endi-1,begdim2,enddim2,begdim3,enddim3)
call set_hbuf_section_to_val
(tape(t)%hlist(f)%hbuf,dimind,0._r8)
tape(t)%hlist(f)%nacs(:,:) = 0
end if
call t_stopf ('h_zero')
return
end subroutine h_zero
!#######################################################################
subroutine dump_field (f, t, restart) 1,7
use cam_pio_utils
, only: get_decomp, pio_subsystem
use dycore
, only: dycore_is
use spmd_utils
, only : iam
integer, intent(in) :: f, t
logical, intent(in) :: restart
!
!-----------------------------------------------------------------------
!
! Purpose: Write a variable to a history tape
!
! Method: If SPMD, first gather the data to the master processor.
! Next, transpose the data to COORDS order (the default).
! Finally, issue the netcdf call to write the variable
!
!-----------------------------------------------------------------------
integer :: bsize, ierr
real(r4), allocatable :: localbuf4(:)
type(io_desc_t), pointer :: int_iodesc, iodesc
type(var_desc_t), pointer :: varid
integer :: iosize
character(len=max_fieldname_len) :: fname
type(column_info) :: tmpcolumn
do i=1,max(ngroup(t),1)
if(restart .and. i>1) exit
varid=>tape(t)%hlist(f)%varid(i)
if(tape(t)%hlist(f)%hwrt_prec==8) then
iosize = pio_double
else
iosize = pio_real
end if
if(.not. restart .and. ngroup(t)>0) then
tmpcolumn = tape(t)%column(i)
if(dycore_is
('LR')) then
fname = tape(t)%hlist(f)%field%name
if(fname .eq. 'US' .or. fname .eq. 'FU_S') then
tmpcolumn%columnlat(1) = tape(t)%column_st(i)%columnlat(1)+1 ! Adding 1 because staggered lat indexing for "xzybuf" starts at 2
tmpcolumn%columnlat(2) = tape(t)%column_st(i)%columnlat(2)+1
tmpcolumn%num_lats = tape(t)%column_st(i)%num_lats
else if(fname .eq. 'VS' .or. fname .eq. 'FV_S') then
tmpcolumn%columnlon = tape(t)%column_st(i)%columnlon
tmpcolumn%num_lons = tape(t)%column_st(i)%num_lons
end if
end if
allocate(iodesc) ! no lookup table for columns
call get_decomp
(iodesc, tape(t)%hlist(f)%field, iosize,column=tmpcolumn)
#ifdef HDEBUG
write(iulog,*)'DUMP_FIELD:',__LINE__,' writing column time indx ', &
nfils(t), ' field id', tape(t)%hlist(f)%varid(i)%varid, &
' name ', trim(tape(t)%hlist(f)%field_column_name(i)), &
'numlons=',tmpcolumn%num_lons,'numlats=', tmpcolumn%num_lats, ' base name ', &
trim(tape(t)%hlist(f)%field%name),' columnlon=', &
tmpcolumn%columnlon, ' columnlat=', tmpcolumn%columnlat
#endif
else
call get_decomp
(iodesc, tape(t)%hlist(f)%field, iosize)
endif
if(restart) then
call pio_setframe(varid, int(1,kind=PIO_Offset))
else
call pio_setframe(varid, int(max(1,nfils(t)),kind=PIO_Offset))
end if
if(tape(t)%hlist(f)%hbuf_prec==8) then
bsize=size(tape(t)%hlist(f)%hbuf%buf8)
if (tape(t)%hlist(f)%hwrt_prec == 8) then
call pio_write_darray(tape(t)%File, varid, &
iodesc, tape(t)%hlist(f)%hbuf%buf8, ierr)
else
allocate(localbuf4(bsize))
localbuf4=real(reshape(tape(t)%hlist(f)%hbuf%buf8,(/bsize/)))
call pio_write_darray(tape(t)%File, varid, &
iodesc, localbuf4, ierr)
deallocate(localbuf4)
end if
else
bsize=size(tape(t)%hlist(f)%hbuf%buf4)
if (tape(t)%hlist(f)%hwrt_prec == 4) then
call pio_write_darray(tape(t)%File, varid, &
iodesc,tape(t)%hlist(f)%hbuf%buf4, ierr)
else
! why would you ever do this?
end if
end if
if(restart) then
if(tape(t)%hlist(f)%field%flag_xyfill) then
call get_decomp
(int_iodesc, tape(t)%hlist(f)%field, pio_int, 1)
call pio_write_darray(tape(t)%File, tape(t)%hlist(f)%nacs_varid, &
int_iodesc, reshape(tape(t)%hlist(f)%nacs, (/size(tape(t)%hlist(f)%nacs)/)), ierr)
else
ierr = pio_put_var(tape(t)%File, tape(t)%hlist(f)%nacs_varid,&
tape(t)%hlist(f)%nacs(1, tape(t)%hlist(f)%field%begdim3))
end if
end if
if(.not. restart .and. ngroup(t)>0) then
call pio_freedecomp(pio_subsystem, iodesc)
deallocate(iodesc) ! no lookup table for columns
end if
end do
return
end subroutine dump_field
!#######################################################################
logical function write_inithist () 3,6
!
!-----------------------------------------------------------------------
!
! Purpose: Set flags that will initiate dump to IC file when OUTFLD and
! WSHIST are called
!
!-----------------------------------------------------------------------
!
use time_manager
, only: get_nstep, get_curr_date, get_step_size, is_last_step
!
! Local workspace
!
integer :: yr, mon, day ! year, month, and day components of
! a date
integer :: nstep ! current timestep number
integer :: ncsec ! current time of day [seconds]
integer :: dtime ! timestep size
!-----------------------------------------------------------------------
write_inithist = .false.
if(is_initfile
()) then
nstep = get_nstep
()
call get_curr_date
(yr, mon, day, ncsec)
if (inithist == '6-HOURLY') then
dtime = get_step_size
()
write_inithist = nstep /= 0 .and. mod( nstep, nint((6._r8*3600._r8)/dtime) ) == 0
elseif(inithist == 'DAILY' ) then
write_inithist = nstep /= 0 .and. ncsec == 0
elseif(inithist == 'MONTHLY' ) then
write_inithist = nstep /= 0 .and. ncsec == 0 .and. day == 1
elseif(inithist == 'YEARLY' ) then
write_inithist = nstep /= 0 .and. ncsec == 0 .and. day == 1 .and. mon == 1
elseif(inithist == 'CAMIOP' ) then
write_inithist = nstep == 0
elseif(inithist == 'ENDOFRUN' ) then
write_inithist = nstep /= 0 .and. is_last_step
()
end if
end if
return
end function write_inithist
!#######################################################################
subroutine wshist (rgnht_in) 2,26
!
!-----------------------------------------------------------------------
!
! Purpose: Driver routine to write fields on history tape t
!
! Method: For variables which do not need to be gathered (SPMD) just issue the netcdf call
! For those that do need to be gathered, call "dump_field" to do the operation.
! Finally, zero the history buffers for each field written.
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
use dycore
, only: dycore_is
use time_manager
, only: get_nstep, get_curr_date, get_curr_time,get_step_size
use chem_surfvals
, only: chem_surfvals_get, chem_surfvals_co2_rad
use solar_data
, only: sol_tsi
logical, intent(in), optional :: rgnht_in(ptapes)
!
! Local workspace
!
character(len=8) :: cdate ! system date
character(len=8) :: ctime ! system time
logical :: rgnht(ptapes), restart
integer t, f ! tape, field indices
integer ret ! return value from netcdf call
integer start ! starting index required by nf_put_vara
integer count1 ! count values required by nf_put_vara
integer startc(2) ! start values required by nf_put_vara (character)
integer countc(2) ! count values required by nf_put_vara (character)
#ifdef HDEBUG
! integer begdim3
! integer enddim3
#endif
integer :: yr, mon, day ! year, month, and day components of a date
integer :: nstep ! current timestep number
integer :: ncdate ! current date in integer format [yyyymmdd]
integer :: ncsec ! current time of day [seconds]
integer :: ndcur ! day component of current time
integer :: nscur ! seconds component of current time
real(r8) :: time ! current time
real(r8) :: tdata(2) ! time interval boundaries
character(len=max_string_len) :: fname ! Filename
logical :: prev ! Label file with previous date rather than current
integer :: ierr
integer :: bsize
integer :: dim1s,dim2s,ncol
#if ( defined BFB_CAM_SCAM_IOP )
integer :: tsec ! day component of current time
integer :: dtime ! seconds component of current time
#endif
!-----------------------------------------------------------------------
if(present(rgnht_in)) then
rgnht=rgnht_in
restart=.true.
tape => restarthistory_tape
else
rgnht=.false.
restart=.false.
tape => history_tape
end if
nstep = get_nstep
()
call get_curr_date
(yr, mon, day, ncsec)
ncdate = yr*10000 + mon*100 + day
call get_curr_time
(ndcur, nscur)
!
! Write time-varying portion of history file header
!
do t=1,mtapes
if (nflds(t) == 0 .or. (restart .and.(.not.rgnht(t)))) cycle
!
! Check if this is the IC file and if it's time to write.
! Else, use "nhtfrq" to determine if it's time to write
! the other history files.
!
if((.not. restart) .or. rgnht(t)) then
if( is_initfile
(file_index=t) ) then
hstwr(t) = write_inithist
()
prev = .false.
else
if (nhtfrq(t) == 0) then
hstwr(t) = nstep /= 0 .and. day == 1 .and. ncsec == 0
prev = .true.
else
hstwr(t) = mod(nstep,nhtfrq(t)) == 0
prev = .false.
end if
end if
end if
if (hstwr(t) .or. (restart .and. rgnht(t))) then
if(masterproc) then
if(is_initfile
(file_index=t)) then
write(iulog,100) yr,mon,day,ncsec
100 format('WSHIST: writing time sample to Initial Conditions h-file', &
' DATE=',i4.4,'/',i2.2,'/',i2.2,' NCSEC=',i6)
else if(hstwr(t)) then
write(iulog,200) nfils(t),t,yr,mon,day,ncsec
200 format('WSHIST: writing time sample ',i3,' to h-file ', &
i1,' DATE=',i4.4,'/',i2.2,'/',i2.2,' NCSEC=',i6)
else if(restart .and. rgnht(t)) then
write(iulog,300) nfils(t),t,yr,mon,day,ncsec
300 format('WSHIST: writing history restart ',i3,' to hr-file ', &
i1,' DATE=',i4.4,'/',i2.2,'/',i2.2,' NCSEC=',i6)
end if
write(iulog,*)
end if
!
! Starting a new volume => define the metadata
!
if (nfils(t)==0 .or. (restart.and.rgnht(t))) then
if(restart) then
fname = interpret_filename_spec
( rhfilename_spec, number=(t-1))
hrestpath(t)=fname
else if(is_initfile
(file_index=t)) then
fname = interpret_filename_spec
( hfilename_spec(t) )
else
fname = interpret_filename_spec
( hfilename_spec(t), number=(t-1), &
prev=prev )
end if
!
! Check that this new filename isn't the same as a previous or current filename
!
do f = 1, mtapes
if (masterproc.and. trim(fname) == trim(nhfil(f)) )then
write(iulog,*)'WSHIST: New filename same as old file = ', trim(fname)
write(iulog,*)'Is there an error in your filename specifiers?'
write(iulog,*)'hfilename_spec(', t, ') = ', hfilename_spec(t)
if ( t /= f )then
write(iulog,*)'hfilename_spec(', f, ') = ', hfilename_spec(f)
end if
call endrun
end if
end do
if(.not. restart) then
nhfil(t) = fname
if(masterproc) write(iulog,*)'WSHIST: nhfil(',t,')=',trim(nhfil(t))
cpath(t) = nhfil(t)
if ( len_trim(nfpath(t)) == 0 ) nfpath(t) = cpath(t)
end if
call h_define
(t, restart)
end if
if(restart) then
start=1
else
nfils(t) = nfils(t) + 1
start = nfils(t)
end if
count1 = 1
ierr = pio_put_var (tape(t)%File, tape(t)%ndcurid,(/start/), (/count1/),(/ndcur/))
ierr = pio_put_var (tape(t)%File, tape(t)%nscurid,(/start/), (/count1/),(/nscur/))
ierr = pio_put_var (tape(t)%File, tape(t)%dateid,(/start/), (/count1/),(/ncdate/))
ierr=pio_put_var (tape(t)%File, tape(t)%co2vmrid,(/start/), (/count1/),(/chem_surfvals_co2_rad
(vmr_in=.true.)/))
ierr=pio_put_var (tape(t)%File, tape(t)%ch4vmrid,(/start/), (/count1/),(/chem_surfvals_get
('CH4VMR')/))
ierr=pio_put_var (tape(t)%File, tape(t)%n2ovmrid,(/start/), (/count1/),(/chem_surfvals_get
('N2OVMR')/))
ierr=pio_put_var (tape(t)%File, tape(t)%f11vmrid,(/start/), (/count1/),(/chem_surfvals_get
('F11VMR')/))
ierr=pio_put_var (tape(t)%File, tape(t)%f12vmrid,(/start/), (/count1/),(/chem_surfvals_get
('F12VMR')/))
ierr=pio_put_var (tape(t)%File, tape(t)%sol_tsiid,(/start/), (/count1/),(/sol_tsi/))
ierr = pio_put_var (tape(t)%File, tape(t)%datesecid,(/start/),(/count1/),(/ncsec/))
#if ( defined BFB_CAM_SCAM_IOP )
dtime = get_step_size
()
tsec=dtime*nstep
ierr = pio_put_var (tape(t)%File, tape(t)%tsecid,(/start/),(/count1/),(/tsec/))
#endif
ierr = pio_put_var (tape(t)%File, tape(t)%nstephid,(/start/),(/count1/),(/nstep/))
time = ndcur + nscur/86400._r8
ierr=pio_put_var (tape(t)%File, tape(t)%timeid, (/start/),(/count1/),(/time/))
startc(1) = 1
startc(2) = start
countc(1) = 2
countc(2) = 1
tdata(1) = beg_time(t)
tdata(2) = time
ierr=pio_put_var (tape(t)%File, tape(t)%tbndid, startc, countc, tdata)
if(.not.restart) beg_time(t) = time ! update beginning time of next interval
startc(1) = 1
startc(2) = start
countc(1) = 8
countc(2) = 1
call datetime
(cdate, ctime)
ierr = pio_put_var (tape(t)%File, tape(t)%date_writtenid, startc, countc, (/cdate/))
ierr = pio_put_var (tape(t)%File, tape(t)%time_writtenid, startc, countc, (/ctime/))
if(.not. restart) then
!$OMP PARALLEL DO PRIVATE (F)
do f=1,nflds(t)
call h_normalize
(f, t) ! Normalized averaged fields and/or put fillvalue
end do
end if
!
! Write field to history tape. Note that this is NOT threaded due to netcdf limitations
!
call t_startf ('dump_field')
do f=1,nflds(t)
call dump_field
(f, t, restart)
end do
call t_stopf ('dump_field')
!
! Zero history buffers and accumulators now that the fields have been written.
!
if(restart) then
do f=1,nflds(t)
if(associated(tape(t)%hlist(f)%varid)) then
deallocate(tape(t)%hlist(f)%varid)
nullify(tape(t)%hlist(f)%varid)
end if
end do
call pio_closefile(tape(t)%File)
tape(t)%File%fh=-1
else
!$OMP PARALLEL DO PRIVATE (F)
do f=1,nflds(t)
call h_zero
(f, t)
end do
end if
end if
end do
return
end subroutine wshist
!#######################################################################
subroutine addvar (ncid, name, xtype , ndims , dimids, vid),1
!
!-----------------------------------------------------------------------
!
! Purpose: Issue the netcdf call to add a variable to the dataset
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
!
! Input arguments
!
type(file_desc_t), intent(inout) :: ncid ! netcdf file id
integer, intent(in) :: xtype ! netcdf type flag
integer, intent(in) :: ndims ! number of dimensions
integer, intent(in) :: dimids(:) ! dimension ids
type(var_desc_t), intent(out) :: vid ! variable ids
character(len=*), intent(in) :: name ! variable name
integer :: ierr
!
! Local workspace
!
type(master_entry), pointer :: listentry
ierr=pio_def_var (ncid, name, xtype , dimids(1:ndims), vid)
listentry=> get_entry_by_name
(masterlinkedlist, name)
if(associated(listentry)) then
ierr=pio_put_att (ncid, vid, 'long_name', listentry%field%long_name)
ierr=pio_put_att (ncid, vid, 'units', listentry%field%units)
end if
!
! Field not found in masterlist: long_name and units attributes unknown so just
! return
!
return
end subroutine addvar
!#######################################################################
subroutine addfld (fname, units, numlev, avgflag, long_name, & 1070,27
decomp_type, flag_xyfill, flag_isccplev, sampling_seq)
!
!-----------------------------------------------------------------------
!
! Purpose: Add a field to the master field list
!
! Method: Put input arguments of field name, units, number of levels, averaging flag, and
! long name into a type entry in the global master field list (masterlist).
!
! Author: CCM Core Group
!
!-----------------------------------------------------------------------
use ppgrid
, only: begchunk, endchunk
use rgrid
, only: nlon
use phys_grid
, only: get_ncols_p, physgrid_set
use dycore
, only: dycore_is
use cam_pio_utils
, only: phys_decomp, dyn_decomp, dyn_stagger_decomp
!
! Arguments
!
character(len=*), intent(in) :: fname ! field name--should be "max_fieldname_len" characters long
! or less
character(len=*), intent(in) :: units ! units of fname--should be 8 chars
character(len=1), intent(in) :: avgflag ! averaging flag
character(len=*), intent(in) :: long_name ! long name of field
integer, intent(in) :: numlev ! number of vertical levels (dimension and loop)
integer, intent(in) :: decomp_type ! decomposition type
logical, intent(in), optional :: flag_xyfill ! non-applicable xy points flagged with fillvalue
logical, intent(in), optional :: flag_isccplev ! levels are ISCCP levels not vertical
character(len=*), intent(in), optional :: sampling_seq ! sampling sequence - if not every timestep,
! how often field is sampled:
! every other; only during LW/SW radiation calcs, etc.
integer dim1s,dim2s ! global size of the first and second horizontal dim.
!
! Local workspace
!
character(len=max_fieldname_len) :: fname_tmp ! local copy of fname
type(master_entry), pointer :: listentry
integer :: c ! chunk (physics ) or latitude (dynamics) index
integer :: ncol ! number of columns per chunk
integer :: beglat, endlat
integer :: beglatxy, endlatxy, beglonxy, endlonxy, beglon, endlon
beglat = get_dyn_grid_parm
('beglat')
endlat = get_dyn_grid_parm
('endlat')
beglon = get_dyn_grid_parm
('beglon')
endlon = get_dyn_grid_parm
('endlon')
beglatxy = get_dyn_grid_parm
('beglatxy')
endlatxy = get_dyn_grid_parm
('endlatxy')
beglonxy = get_dyn_grid_parm
('beglonxy')
endlonxy = get_dyn_grid_parm
('endlonxy')
!
! Ensure that required grid info is available now
!
select case (decomp_type)
case (phys_decomp)
if (.not. physgrid_set) then
call endrun
('ADDFLD: Attempt to add field '//fname//' to masterlist before physics grid set')
end if
case (dyn_decomp)
if (.not. dyndecomp_set) then
call endrun
('ADDFLD: Attempt to add field '//fname//' to masterlist before dynamics grid set')
end if
end select
call get_horiz_grid_dim_d
(dim1s,dim2s)
!
! Ensure that new field name is not all blanks
!
if (len_trim(fname)==0) then
call endrun
('ADDFLD: blank field name not allowed')
end if
!
! Ensure that new field name is not longer than allowed
! (strip "&IC" suffix if it exists)
!
fname_tmp = fname
fname_tmp = strip_suffix
(fname_tmp)
if (len_trim(fname_tmp) > fieldname_len) then
write(iulog,*)'ADDFLD: field name cannot be longer than ',fieldname_len,' characters long'
write(iulog,*)'Field name: ',fname
call endrun
()
end if
!
! Ensure that new field doesn't already exist
!
listentry => get_entry_by_name
(masterlinkedlist, fname)
if(associated(listentry)) then
call endrun
('ADDFLD: '//fname//' already on list')
end if
!
! Add field to Master Field List arrays fieldn and iflds
!
allocate(listentry)
listentry%field%name = fname
listentry%field%long_name = long_name
listentry%field%units = units
listentry%field%numlev = numlev
listentry%field%decomp_type = decomp_type
listentry%htapeindx(:)=-1
listentry%act_sometape = .false.
listentry%actflag(:) = .false.
!
! Indicate sampling sequence of field (i.e., how often "outfld" is called)
! If not every timestep (default), then give a descriptor indicating the
! sampling pattern. Currently, the only valid value is "rad_lwsw" for sampling
! during LW/SW radiation timesteps only
!
if (present(sampling_seq)) then
listentry%field%sampling_seq = sampling_seq
else
listentry%field%sampling_seq = ' '
end if
!
! Whether to apply xy fillvalue: default is false
!
if (present(flag_xyfill)) then
listentry%field%flag_xyfill = flag_xyfill
else
listentry%field%flag_xyfill = .false.
end if
!
! Whether level dimension is ISCCP (currently 49) or CAM
!
if (present(flag_isccplev)) then
listentry%field%flag_isccplev = flag_isccplev
else
listentry%field%flag_isccplev = .false.
end if
if (listentry%field%flag_isccplev .and. numlev /= npres*ntau) then
write(iulog,*)'ADDFLD: Number of ISCCP levels must be ',npres*ntau, ' got ', numlev
call endrun
()
end if
!
! Dimension history info based on decomposition type (dynamics or physics)
!
select case (decomp_type)
case (phys_decomp)
listentry%field%begdim1 = 1
listentry%field%enddim1 = pcols
listentry%field%begdim2 = 1
listentry%field%enddim2 = numlev
listentry%field%begdim3 = begchunk
listentry%field%enddim3 = endchunk
allocate (listentry%field%colperdim3(begchunk:endchunk))
do c=begchunk,endchunk
ncol = get_ncols_p
(c)
listentry%field%colperdim3(c) = ncol
end do
case (dyn_stagger_decomp)
listentry%field%begdim1 = beglonxy
listentry%field%enddim1 = endlonxy
listentry%field%begdim2 = 1
listentry%field%enddim2 = numlev
listentry%field%begdim3 = beglatxy
listentry%field%enddim3 = endlatxy
nullify (listentry%field%colperdim3)
case (dyn_decomp)
if ( dycore_is
('LR') )then
listentry%field%begdim1 = beglonxy
listentry%field%enddim1 = endlonxy
listentry%field%begdim2 = 1
listentry%field%enddim2 = numlev
listentry%field%begdim3 = beglatxy
listentry%field%enddim3 = endlatxy
nullify (listentry%field%colperdim3)
else
listentry%field%begdim3 = beglat
listentry%field%enddim3 = endlat
listentry%field%begdim2 = 1
listentry%field%enddim2 = numlev
listentry%field%begdim1 = 1
listentry%field%enddim1 = dim1s
allocate (listentry%field%colperdim3(beglat:endlat))
do c=beglat,endlat
listentry%field%colperdim3(c) = nlon(c)
end do
endif
case default
write(iulog,*)'ADDFLD: unknown decomp_type=', decomp_type
call endrun
()
end select
!
! These 2 fields are used only in master field list, not runtime field list
!
listentry%avgflag(:) = avgflag
listentry%actflag(:) = .false.
select case (avgflag)
case ('A')
listentry%time_op(:) = 'mean'
case ('B')
listentry%time_op(:) = 'mean00z'
case ('I')
listentry%time_op(:) = ' '
case ('X')
listentry%time_op(:) = 'maximum'
case ('M')
listentry%time_op(:) = 'minimum'
case default
call endrun
('ADDFLD: unknown avgflag='//avgflag)
end select
nullify(listentry%next_entry)
call add_entry_to_master
(listentry)
return
end subroutine addfld
subroutine add_entry_to_master( newentry) 1
type(master_entry), target, intent(in) :: newentry
type(master_entry), pointer :: listentry
if(associated(masterlinkedlist)) then
listentry => masterlinkedlist
do while(associated(listentry%next_entry))
listentry=>listentry%next_entry
end do
listentry%next_entry=>newentry
else
masterlinkedlist=>newentry
end if
end subroutine add_entry_to_master
!#######################################################################
subroutine wrapup (rstwr, nlend) 1,10
!
!-----------------------------------------------------------------------
!
! Purpose:
! Close history files.
!
! Method:
! This routine will close any full hist. files
! or any hist. file that has data on it when restart files are being
! written.
! If a partially full history file was disposed (for restart
! purposes), then wrapup will open that unit back up and position
! it for appending new data.
!
! Original version: CCM2
!
!-----------------------------------------------------------------------
!
use shr_kind_mod
, only: r8 => shr_kind_r8
use pspect
use ioFileMod
use time_manager
, only: get_nstep, get_curr_date, get_curr_time
use cam_pio_utils
, only: cam_pio_openfile
!
! Input arguments
!
logical, intent(in) :: rstwr ! true => restart files are written this timestep
logical, intent(in) :: nlend ! Flag if time to end
!
! Local workspace
!
integer :: nstep ! current timestep number
integer :: ncdate ! current date in integer format [yyyymmdd]
integer :: ncsec ! time of day relative to current date [seconds]
integer :: ndcur ! days component of current time
integer :: nscur ! seconds component of current time
integer :: yr, mon, day ! year, month, day components of a date
logical lfill (ptapes) ! Is history file ready to dispose?
logical hdispose(ptapes) ! Primary file disposed
logical lhdisp ! true => history file is disposed
logical lhfill ! true => history file is full
integer t ! History file number
integer f
integer :: ierr
real(r8) tday ! Model day number for printout
!-----------------------------------------------------------------------
tape => history_tape
nstep = get_nstep
()
call get_curr_date
(yr, mon, day, ncsec)
ncdate = yr*10000 + mon*100 + day
call get_curr_time
(ndcur, nscur)
!
!-----------------------------------------------------------------------
! Dispose history files.
!-----------------------------------------------------------------------
!
! Begin loop over mtapes (the no. of declared history files - primary
! and auxiliary). This loop disposes a history file to Mass Store
! when appropriate.
!
do t=1,mtapes
if (nflds(t) == 0) cycle
hdispose(t) = .false.
lfill(t) = .false.
!
! Find out if file is full
!
if (hstwr(t) .and. nfils(t) >= mfilt(t)) then
lfill(t) = .true.
endif
!
! Dispose history file if
! 1) file is filled or
! 2) this is the end of run and file has data on it or
! 3) restarts are being put out and history file has data on it
!
if (lfill(t) .or. (nlend .and. nfils(t) >= 1) .or. (rstwr .and. nfils(t) >= 1)) then
!
! Dispose history file
!
hdispose(t) = .true.
!
! Is this the 0 timestep data of a monthly run?
! If so, just close primary unit do not dispose.
!
if (masterproc) write(iulog,*)'WRAPUP: nf_close(',t,')=',trim(nhfil(t))
if(tape(t)%File%fh>-1) then
if (nlend .or. lfill(t)) then
do f=1,nflds(t)
if (associated(tape(t)%hlist(f)%varid)) then
deallocate(tape(t)%hlist(f)%varid)
nullify(tape(t)%hlist(f)%varid)
end if
end do
end if
call PIO_CloseFile (tape(t)%File)
tape(t)%File%fh=-1
end if
if (nhtfrq(t) /= 0 .or. nstep > 0) then
!
! Print information concerning model output.
! Model day number = iteration number of history file data * delta-t / (seconds per day)
!
tday = ndcur + nscur/86400._r8
if(masterproc) then
if (t==1) then
write(iulog,*)' Primary history file'
else
write(iulog,*)' Auxiliary history file number ', t-1
end if
write(iulog,9003)nstep,nfils(t),tday
write(iulog,9004)
end if
!
! Auxilary files may have been closed and saved off without being full.
! We must reopen the files and position them for more data.
! Must position auxiliary files if not full
!
if (.not.nlend .and. .not.lfill(t)) then
call cam_PIO_openfile
(tape(t)%File, nhfil(t), PIO_WRITE)
call h_inquire
(t)
end if
endif ! if 0 timestep of montly run****
end if ! if time dispose history fiels***
end do ! do mtapes
!
! Reset number of files on each history tape
!
do t=1,mtapes
if (nflds(t) == 0) cycle
lhfill = hstwr(t) .and. nfils(t) >= mfilt(t)
lhdisp = lhfill .or. (nlend .and. nfils(t) >= 1) .or. &
(rstwr .and. nfils(t) >= 1)
if (lhfill.and.lhdisp) then
nfils(t) = 0
endif
end do
return
9003 format(' Output at NSTEP = ',i10,/, &
' Number of time samples on this file = ',i10,/, &
' Model Day = ',f10.2)
9004 format('---------------------------------------')
end subroutine wrapup
!#######################################################################
subroutine allocate_hbuf2d (hbuf, dimind, hbuf_prec) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Allocate memory for the 2-D history buffer of the right precision.
! Nullify the other buffer.
!
!-----------------------------------------------------------------------
!
type (hbuffer_2d) :: hbuf ! history buffer
type (dim_index_2d) :: dimind ! 2-D dimension index
integer, intent(in) :: hbuf_prec ! precision for history buffer
real(r8), pointer :: b8(:,:)
real(r4), pointer :: b4(:,:)
if (hbuf_prec == 8) then
nullify (hbuf%buf4)
allocate (hbuf%buf8(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2))
hbuf%buf8=0._r8
#ifdef HBUF_DEBUG
b8=>hbuf%buf8
if(masterproc) write(iulog,*) __FILE__,__LINE__,'allocate: ',loc(b8)
#endif
else
allocate (hbuf%buf4(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2))
hbuf%buf4=0._r4
nullify (hbuf%buf8)
#ifdef HBUF_DEBUG
b4=>hbuf%buf4
if(masterproc) write(iulog,*) __FILE__,__LINE__,'allocate: ',loc(b4)
#endif
end if
end subroutine allocate_hbuf2d
!#######################################################################
subroutine allocate_hbuf3d (hbuf, dimind, hbuf_prec) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Allocate memory for the 3-D history buffer of the right precision
! Nullify the other buffer.
!
!-----------------------------------------------------------------------
type (hbuffer_3d) :: hbuf ! history buffer
type (dim_index_3d) :: dimind ! 3-D dimension index
integer, intent(in) :: hbuf_prec ! precision for history buffer
real(r8), pointer :: b8(:,:,:)
real(r4), pointer :: b4(:,:,:)
if (hbuf_prec == 8) then
nullify (hbuf%buf4)
allocate (hbuf%buf8(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2, &
dimind%beg3:dimind%end3))
hbuf%buf8=0._r8
#ifdef HBUF_DEBUG
b8=>hbuf%buf8
if(masterproc) write(iulog,*) __FILE__,__LINE__,'allocate: ',loc(b8)
#endif
else
allocate (hbuf%buf4(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2, &
dimind%beg3:dimind%end3))
nullify (hbuf%buf8)
hbuf%buf4=0._r4
#ifdef HBUF_DEBUG
b4=>hbuf%buf4
if(masterproc) write(iulog,*) __FILE__,__LINE__,'allocate: ',loc(b4)
#endif
end if
end subroutine allocate_hbuf3d
!#######################################################################
subroutine deallocate_hbuf2d (hbuf) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Deallocate memory for 2-D history buffer
!
!-----------------------------------------------------------------------
!
type (hbuffer_2d) :: hbuf ! history buffer
real(r8), pointer :: b8(:,:)
real(r4), pointer :: b4(:,:)
if (associated(hbuf%buf8)) then
#ifdef HBUF_DEBUG
b8=>hbuf%buf8
if(masterproc) write(iulog,*) __FILE__,__LINE__,'deallocate: ',loc(b8)
#endif
deallocate (hbuf%buf8)
else if (associated(hbuf%buf4)) then
#ifdef HBUF_DEBUG
b4=>hbuf%buf4
if(masterproc) write(iulog,*) __FILE__,__LINE__,'deallocate: ',loc(b4)
#endif
deallocate (hbuf%buf4)
end if
end subroutine deallocate_hbuf2d
!#######################################################################
subroutine deallocate_hbuf3d (hbuf) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Deallocate memory for 3-D history buffer
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d) :: hbuf ! history buffer
real(r8), pointer :: b8(:,:,:)
real(r4), pointer :: b4(:,:,:)
if (associated(hbuf%buf8)) then
#ifdef HBUF_DEBUG
b8=>hbuf%buf8
if(masterproc) write(iulog,*) __FILE__,__LINE__,'deallocate: ',loc(b8)
#endif
deallocate (hbuf%buf8)
else if (associated(hbuf%buf4)) then
#ifdef HBUF_DEBUG
b4=>hbuf%buf4
if(masterproc) write(iulog,*) __FILE__,__LINE__,'deallocate: ',loc(b4)
#endif
deallocate (hbuf%buf4)
end if
end subroutine deallocate_hbuf3d
!#######################################################################
subroutine nullify_hbuf2d (hbuf) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Nullify 2-D history buffer pointers
!
!-----------------------------------------------------------------------
!
type (hbuffer_2d) :: hbuf ! history buffer
nullify (hbuf%buf8, hbuf%buf4)
end subroutine nullify_hbuf2d
!#######################################################################
subroutine nullify_hbuf3d (hbuf) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Nullify 3-D history buffer pointers
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d) :: hbuf ! history buffer
nullify (hbuf%buf8, hbuf%buf4)
end subroutine nullify_hbuf3d
!#######################################################################
subroutine hbuf_assigned_to_hbuf (hbuf1, hbuf2) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Set hbuf1 to hbuf2 (copy the contents).
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d), intent(inout) :: hbuf1 ! history buffer
type (hbuffer_3d), intent(in ) :: hbuf2 ! history buffer
if (associated(hbuf2%buf8)) then
hbuf1%buf8 = hbuf2%buf8
else if (associated(hbuf2%buf4)) then
hbuf1%buf4 = hbuf2%buf4
end if
end subroutine hbuf_assigned_to_hbuf
!#######################################################################
subroutine hbuf_assigned_to_real8 (hbuf, scalar) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Set appropriate history buffer to the value, scalar
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d), intent(inout) :: hbuf ! history buffer
real(r8), intent(in) :: scalar ! scalar real
if (associated(hbuf%buf8)) then
hbuf%buf8 = scalar
else if (associated(hbuf%buf4)) then
hbuf%buf4 = scalar
end if
end subroutine hbuf_assigned_to_real8
!#######################################################################
subroutine set_hbuf_section_to_val (hbuf, dimind, scalar) 2
!
!-----------------------------------------------------------------------
!
! Purpose: Set section of appropriate history buffer to scalar
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d), intent(inout) :: hbuf ! history buffer
type (dim_index_3d), intent(in ) :: dimind ! 3-D dimension index
real(r8), intent(in) :: scalar ! scalar real
if (associated(hbuf%buf8)) then
hbuf%buf8(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2, &
dimind%beg3:dimind%end3) = scalar
else if (associated(hbuf%buf4)) then
hbuf%buf4(dimind%beg1:dimind%end1, dimind%beg2:dimind%end2, &
dimind%beg3:dimind%end3) = scalar
end if
end subroutine set_hbuf_section_to_val
!#######################################################################
subroutine assoc_hbuf2d_with_hbuf3d (hbuf2d, hbuf3d, c) 1
!
!-----------------------------------------------------------------------
!
! Purpose: Associate 2-D hbuf2d with 3-D hbuf3d of column c.
! Nullify the other buffer of hbuf2d.
!
!-----------------------------------------------------------------------
!
type (hbuffer_2d), intent(out) :: hbuf2d ! 2-D history buffer
type (hbuffer_3d), intent(in ) :: hbuf3d ! 3-D history buffer
integer, intent(in) :: c ! chunk (or lat) index
if (associated(hbuf3d%buf8)) then
hbuf2d%buf8 => hbuf3d%buf8(:,:,c)
nullify (hbuf2d%buf4)
else if (associated(hbuf3d%buf4)) then
hbuf2d%buf4 => hbuf3d%buf4(:,:,c)
nullify (hbuf2d%buf8)
end if
end subroutine assoc_hbuf2d_with_hbuf3d
!#######################################################################
subroutine assoc_hbuf_with_nothing (hbuf, hbuf_prec)
!
!-----------------------------------------------------------------------
!
! Purpose: Associate the appropriate 3-D hbuf pointer with nothing.
! Nullify the other pointer.
!
!-----------------------------------------------------------------------
!
type (hbuffer_3d), intent(out) :: hbuf ! 3-D history buffer
integer, intent(in) :: hbuf_prec ! hbuffer precision
if (hbuf_prec == 8) then
hbuf%buf8 => nothing_r8
nullify (hbuf%buf4)
else if (hbuf_prec == 4) then
hbuf%buf4 => nothing_r4
nullify (hbuf%buf8)
end if
return
end subroutine assoc_hbuf_with_nothing
!#######################################################################
subroutine hbuf_accum_inst (buf4, buf8, field, nacs, dimind, idim, flag_xyfill) 1,1
!
!-----------------------------------------------------------------------
!
! Purpose: Accumulate instantaneous values of field in 2-D hbuf.
! Set accumulation counter to 1.
!
!-----------------------------------------------------------------------
!
real(r4), pointer :: buf4(:,:) ! 2-D history buffer
real(r8), pointer :: buf8(:,:) ! 2-D history buffer
integer, pointer :: nacs(:) ! accumulation counter
integer, intent(in) :: idim ! Longitude dimension of field array
logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue
real(r8), intent(in ) :: field(idim,*) ! real*8 array
type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index
!
! Local indices
!
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer :: ieu, jeu ! number of elements in each dimension
integer :: i, k ! loop indices
logical :: bad ! flag indicates input field fillvalues not applied consistently
! with vertical level
ib = dimind%beg1
ie = dimind%end1
jb = dimind%beg2
je = dimind%end2
ieu = ie-ib+1
jeu = je-jb+1
if (associated(buf8)) then
do k=1,jeu
do i=1,ieu
buf8(i,k) = field(i,k)
end do
end do
else if (associated(buf4)) then
do k=1,jeu
do i=1,ieu
buf4(i,k) = field(i,k)
end do
end do
end if
if (flag_xyfill) then
do i=1,ieu
if (field(i,1) == fillvalue) then
nacs(i) = 0
else
nacs(i) = 1
end if
call check_accum
(field, idim, ieu, jeu)
end do
else
nacs(1) = 1
end if
return
end subroutine hbuf_accum_inst
!#######################################################################
subroutine check_accum (field, idim, ieu, jeu) 5,1
!
integer, intent(in) :: idim
real(r8), intent(in) :: field(idim,*) ! real*8 array
integer, intent(in) :: ieu,jeu ! loop ranges
logical :: bad
integer :: i,k
!
! For multilevel fields ensure that all levels have fillvalue applied consistently
!
bad = .false.
do k=2,jeu
do i=1,ieu
if (field(i,1) == fillvalue .and. field(i,k) /= fillvalue .or. &
field(i,1) /= fillvalue .and. field(i,k) == fillvalue) then
bad = .true.
end if
end do
end do
if (bad) then
call endrun
('CHECK_ACCUM: inconsistent level values')
end if
return
end subroutine check_accum
!#######################################################################
subroutine hbuf_accum_add (buf4, buf8, field, nacs, dimind, idim, flag_xyfill) 1,1
!
!-----------------------------------------------------------------------
!
! Purpose: Add the values of field to 2-D hbuf.
! Increment accumulation counter by 1.
!
!-----------------------------------------------------------------------
!
real(r4), pointer :: buf4(:,:) ! 2-D history buffer
real(r8), pointer :: buf8(:,:) ! 2-D history buffer
integer, pointer :: nacs(:) ! accumulation counter
integer, intent(in) :: idim ! Longitude dimension of field array
logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue
real(r8), intent(in ) :: field(idim,*) ! real*8 array
type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index
!
! Local indices
!
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer :: ieu, jeu ! number of elements in each dimension
integer :: i,k ! indices
ib = dimind%beg1
ie = dimind%end1
jb = dimind%beg2
je = dimind%end2
ieu = ie-ib+1
jeu = je-jb+1
if (flag_xyfill) then
if (associated(buf8)) then
do k=1,jeu
do i=1,ieu
if (field(i,k) /= fillvalue) then
buf8(i,k) = buf8(i,k) + field(i,k)
end if
end do
end do
else if (associated(buf4)) then
do k=1,jeu
do i=1,ieu
if (field(i,k) /= fillvalue) then
buf4(i,k) = buf4(i,k) + field(i,k)
end if
end do
end do
end if
!
! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs
!
call check_accum
(field, idim, ieu, jeu)
do i=1,ieu
if (field(i,1) /= fillvalue) then
nacs(i) = nacs(i) + 1
end if
end do
else
if (associated(buf8)) then
do k=1,jeu
do i=1,ieu
buf8(i,k) = buf8(i,k) + field(i,k)
end do
end do
else if (associated(buf4)) then
do k=1,jeu
do i=1,ieu
buf4(i,k) = buf4(i,k) + field(i,k)
end do
end do
end if
nacs(1) = nacs(1) + 1
end if
return
end subroutine hbuf_accum_add
!#######################################################################
subroutine hbuf_accum_add00z (buf4, buf8, field, nacs, dimind, idim, flag_xyfill) 1,3
!
!-----------------------------------------------------------------------
!
! Purpose: Add the values of field to 2-D hbuf, only of time is 00z.
! Increment accumulation counter by 1.
!
!-----------------------------------------------------------------------
!
use time_manager
, only: get_curr_date
real(r4), pointer :: buf4(:,:) ! 2-D history buffer
real(r8), pointer :: buf8(:,:) ! 2-D history buffer
integer, pointer :: nacs(:) ! accumulation counter
integer, intent(in) :: idim ! Longitude dimension of field array
logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue
real(r8), intent(in ) :: field(idim,*) ! real*8 array
type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index
!
! Local indices
!
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer :: ieu, jeu ! number of elements in each dimension
integer :: i,k ! indices
integer :: yr, mon, day, tod
! get the time of day, return if not 00z
call get_curr_date
(yr,mon,day,tod)
if (tod /= 0) return
ib = dimind%beg1
ie = dimind%end1
jb = dimind%beg2
je = dimind%end2
ieu = ie-ib+1
jeu = je-jb+1
if (flag_xyfill) then
if (associated(buf8)) then
do k=1,jeu
do i=1,ieu
if (field(i,k) /= fillvalue) then
buf8(i,k) = buf8(i,k) + field(i,k)
end if
end do
end do
else if (associated(buf4)) then
do k=1,jeu
do i=1,ieu
if (field(i,k) /= fillvalue) then
buf4(i,k) = buf4(i,k) + field(i,k)
end if
end do
end do
end if
!
! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs
!
call check_accum
(field, idim, ieu, jeu)
do i=1,ieu
if (field(i,1) /= fillvalue) then
nacs(i) = nacs(i) + 1
end if
end do
else
if (associated(buf8)) then
do k=1,jeu
do i=1,ieu
buf8(i,k) = buf8(i,k) + field(i,k)
end do
end do
else if (associated(buf4)) then
do k=1,jeu
do i=1,ieu
buf4(i,k) = buf4(i,k) + field(i,k)
end do
end do
end if
nacs(1) = nacs(1) + 1
end if
return
end subroutine hbuf_accum_add00z
!#######################################################################
subroutine hbuf_accum_max (buf4, buf8, field, nacs, dimind, idim, flag_xyfill) 1,1
!
!-----------------------------------------------------------------------
!
! Purpose: Accumulate the maximum values of field in 2-D hbuf
! Set accumulation counter to 1.
!
!-----------------------------------------------------------------------
!
real(r4), pointer :: buf4(:,:) ! 2-D history buffer
real(r8), pointer :: buf8(:,:) ! 2-D history buffer
integer, pointer :: nacs(:) ! accumulation counter
integer, intent(in) :: idim ! Longitude dimension of field array
logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue
real(r8), intent(in ) :: field(idim,*) ! real*8 array
type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index
!
! Local indices
!
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer :: ieu, jeu ! number of elements in each dimension
integer :: i, k
ib = dimind%beg1
ie = dimind%end1
jb = dimind%beg2
je = dimind%end2
ieu = ie-ib+1
jeu = je-jb+1
if (associated(buf8)) then
if (flag_xyfill) then
do k=1,jeu
do i=1,ieu
if (nacs(i) == 0) then
buf8(i,k) = -huge (buf8)
end if
if (field(i,k) > buf8(i,k) .and. field(i,k) /= fillvalue) then
buf8(i,k) = field(i,k)
end if
end do
end do
else
do k=1,jeu
do i=1,ieu
if (nacs(1) == 0) then
buf8(i,k) = -huge (buf8)
end if
if (field(i,k) > buf8(i,k)) then
buf8(i,k) = field(i,k)
end if
end do
end do
end if
else if (associated(buf4)) then
if (flag_xyfill) then
do k=1,jeu
do i=1,ieu
if (nacs(i) == 0) then
buf4(i,k) = -huge (buf4)
end if
if (field(i,k) > buf4(i,k) .and. field(i,k) /= fillvalue) then
buf4(i,k) = field(i,k)
end if
end do
end do
else
do k=1,jeu
do i=1,ieu
if (nacs(1) == 0) then
buf4(i,k) = -huge (buf4)
end if
if (field(i,k) > buf4(i,k)) then
buf4(i,k) = field(i,k)
end if
end do
end do
end if
end if
if (flag_xyfill) then
call check_accum
(field, idim, ieu, jeu)
do i=1,ieu
if (field(i,1) /= fillvalue) then
nacs(i) = 1
end if
end do
else
nacs(1) = 1
end if
return
end subroutine hbuf_accum_max
!#######################################################################
subroutine hbuf_accum_min (buf4, buf8, field, nacs, dimind, idim, flag_xyfill) 1,1
!
!-----------------------------------------------------------------------
!
! Purpose: Accumulate the minimum values of field in 2-D hbuf
! Set accumulation counter to 1.
!
!-----------------------------------------------------------------------
!
real(r4), pointer :: buf4(:,:) ! 2-D history buffer
real(r8), pointer :: buf8(:,:) ! 2-D history buffer
integer, pointer :: nacs(:) ! accumulation counter
integer, intent(in) :: idim ! Longitude dimension of field array
logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue
real(r8), intent(in ) :: field(idim,*) ! real*8 array
type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index
!
! Local indices
!
integer :: ib, ie ! beginning and ending indices of first dimension
integer :: jb, je ! beginning and ending indices of second dimension
integer :: ieu, jeu ! number of elements in each dimension
integer :: i, k
ib = dimind%beg1
ie = dimind%end1
jb = dimind%beg2
je = dimind%end2
ieu = ie-ib+1
jeu = je-jb+1
if (associated(buf8)) then
if (flag_xyfill) then
do k=1,jeu
do i=1,ieu
if (nacs(i) == 0) then
buf8(i,k) = +huge (buf8)
end if
if (field(i,k) < buf8(i,k) .and. field(i,k) /= fillvalue) then
buf8(i,k) = field(i,k)
end if
end do
end do
else
do k=1,jeu
do i=1,ieu
if (nacs(1) == 0) then
buf8(i,k) = +huge (buf8)
end if
if (field(i,k) < buf8(i,k)) then
buf8(i,k) = field(i,k)
end if
end do
end do
end if
else if (associated(buf4)) then
if (flag_xyfill) then
do k=1,jeu
do i=1,ieu
if (nacs(i) == 0) then
buf4(i,k) = +huge (buf4)
end if
if (field(i,k) < buf4(i,k) .and. field(i,k) /= fillvalue) then
buf4(i,k) = field(i,k)
end if
end do
end do
else
do k=1,jeu
do i=1,ieu
if (nacs(1) == 0) then
buf4(i,k) = +huge (buf4)
end if
if (field(i,k) < buf4(i,k)) then
buf4(i,k) = field(i,k)
end if
end do
end do
end if
end if
if (flag_xyfill) then
call check_accum
(field, idim, ieu, jeu)
do i=1,ieu
if (field(i,1) /= fillvalue) then
nacs(i) = 1
end if
end do
else
nacs(1) = 1
end if
return
end subroutine hbuf_accum_min
integer function gen_hash_key(string) 3
!dir$ INLINENEVER gen_hash_key
!
!-----------------------------------------------------------------------
!
! Purpose: Generate a hash key on the interval [0 .. tbl_hash_pri_sz-1]
! given a character string.
!
! Algorithm is a variant of perl's internal hashing function.
!
!-----------------------------------------------------------------------
!
implicit none
!
! Arguments:
!
character(len=*), intent(in) :: string
!
! Local.
!
integer :: hash
integer :: i
hash = gen_hash_key_offset
if ( len(string) /= 19 ) then
!
! Process arbitrary string length.
!
do i = 1, len(string)
hash = ieor(hash , (ichar(string(i:i)) * tbl_gen_hash_key(iand(i-1,tbl_max_idx))))
end do
else
!
! Special case string length = 19
!
hash = ieor(hash , ichar(string(1:1)) * 61)
hash = ieor(hash , ichar(string(2:2)) * 59)
hash = ieor(hash , ichar(string(3:3)) * 53)
hash = ieor(hash , ichar(string(4:4)) * 47)
hash = ieor(hash , ichar(string(5:5)) * 43)
hash = ieor(hash , ichar(string(6:6)) * 41)
hash = ieor(hash , ichar(string(7:7)) * 37)
hash = ieor(hash , ichar(string(8:8)) * 31)
hash = ieor(hash , ichar(string(9:9)) * 29)
hash = ieor(hash , ichar(string(10:10)) * 23)
hash = ieor(hash , ichar(string(11:11)) * 17)
hash = ieor(hash , ichar(string(12:12)) * 13)
hash = ieor(hash , ichar(string(13:13)) * 11)
hash = ieor(hash , ichar(string(14:14)) * 7)
hash = ieor(hash , ichar(string(15:15)) * 3)
hash = ieor(hash , ichar(string(16:16)) * 1)
hash = ieor(hash , ichar(string(17:17)) * 61)
hash = ieor(hash , ichar(string(18:18)) * 59)
hash = ieor(hash , ichar(string(19:19)) * 53)
end if
gen_hash_key = iand(hash, tbl_hash_pri_sz-1)
return
end function gen_hash_key
!#######################################################################
integer function get_masterlist_indx(fldname) 2,3
!
!-----------------------------------------------------------------------
!
! Purpose: Return the the index of the field's name on the master file list.
!
! If the field is not found on the masterlist, return -1.
!
!-----------------------------------------------------------------------
!
! Arguments:
!
character(len=*), intent(in) :: fldname
!
! Local.
!
integer :: hash_key
integer :: ff
integer :: ii
integer :: io ! Index of overflow chain in overflow table
integer :: in ! Number of entries on overflow chain
hash_key = gen_hash_key
(fldname)
ff = tbl_hash_pri(hash_key)
if ( ff < 0 ) then
io = abs(ff)
in = tbl_hash_oflow(io)
do ii = 1, in
ff = tbl_hash_oflow(io+ii)
if ( masterlist(ff)%thisentry%field%name == fldname ) exit
end do
end if
if (ff == 0) then
! fldname generated a hash key that doesn't have an entry in tbl_hash_pri.
! This means that fldname isn't in the masterlist
call endrun
('GET_MASTERLIST_INDX: attemping to output field '//fldname//' not on master list')
end if
if (associated(masterlist(ff)%thisentry) .and. masterlist(ff)%thisentry%field%name /= fldname ) then
call endrun
('GET_MASTERLIST_INDX: error finding field '//fldname//' on master list')
end if
get_masterlist_indx = ff
return
end function get_masterlist_indx
!#######################################################################
subroutine bld_outfld_hash_tbls() 2,4
!
!-----------------------------------------------------------------------
!
! Purpose: Build primary and overflow hash tables for outfld processing.
!
! Steps:
! 1) Foreach field on masterlist, find all collisions.
! 2) Given the number of collisions, verify overflow table has sufficient
! space.
! 3) Build primary and overflow indices.
!
!-----------------------------------------------------------------------
!
! Local.
!
integer :: ff
integer :: ii
integer :: itemp
integer :: ncollisions
integer :: hash_key
type(master_entry), pointer :: listentry
!
! 1) Find all collisions.
!
tbl_hash_pri = 0
ff=0
allocate(masterlist(nfmaster))
listentry=>masterlinkedlist
do while(associated(listentry))
ff=ff+1
masterlist(ff)%thisentry=>listentry
listentry=>listentry%next_entry
end do
if(ff /= nfmaster) then
write(iulog,*) 'nfmaster = ',nfmaster, ' ff=',ff
call endrun
('mismatch in expected size of nfmaster')
end if
do ff = 1, nfmaster
hash_key = gen_hash_key
(masterlist(ff)%thisentry%field%name)
tbl_hash_pri(hash_key) = tbl_hash_pri(hash_key) + 1
end do
!
! 2) Count number of collisions and define start of a individual
! collision's chain in overflow table. A collision is defined to be any
! location in tbl_hash_pri that has a value > 1.
!
ncollisions = 0
do ii = 0, tbl_hash_pri_sz-1
if ( tbl_hash_pri(ii) > 1 ) then ! Define start of chain in O.F. table
itemp = tbl_hash_pri(ii)
tbl_hash_pri(ii) = -(ncollisions + 1)
ncollisions = ncollisions + itemp + 1
end if
end do
if ( ncollisions > tbl_hash_oflow_sz ) then
write(iulog,*) 'BLD_OUTFLD_HASH_TBLS: ncollisions > tbl_hash_oflow_sz', &
ncollisions, tbl_hash_oflow_sz
call endrun
()
end if
!
! 3) Build primary and overflow tables.
! i - set collisions in tbl_hash_pri to point to their respective
! chain in the overflow table.
!
tbl_hash_oflow = 0
do ff = 1, nfmaster
hash_key = gen_hash_key
(masterlist(ff)%thisentry%field%name)
if ( tbl_hash_pri(hash_key) < 0 ) then
ii = abs(tbl_hash_pri(hash_key))
tbl_hash_oflow(ii) = tbl_hash_oflow(ii) + 1
tbl_hash_oflow(ii+tbl_hash_oflow(ii)) = ff
else
tbl_hash_pri(hash_key) = ff
end if
end do
!
! Dump out primary and overflow hashing tables.
!
! if ( masterproc ) then
! do ii = 0, tbl_hash_pri_sz-1
! if ( tbl_hash_pri(ii) /= 0 ) write(iulog,666) 'tbl_hash_pri', ii, tbl_hash_pri(ii)
! end do
!
! do ii = 1, tbl_hash_oflow_sz
! if ( tbl_hash_oflow(ii) /= 0 ) write(iulog,666) 'tbl_hash_oflow', ii, tbl_hash_oflow(ii)
! end do
!
! itemp = 0
! ii = 1
! do
! if ( tbl_hash_oflow(ii) == 0 ) exit
! itemp = itemp + 1
! write(iulog,*) 'Overflow chain ', itemp, ' has ', tbl_hash_oflow(ii), ' entries:'
! do ff = 1, tbl_hash_oflow(ii) ! dump out colliding names on this chain
! write(iulog,*) ' ', ff, ' = ', tbl_hash_oflow(ii+ff), &
! ' ', masterlist(tbl_hash_oflow(ii+ff))%thisentry%field%name
! end do
! ii = ii + tbl_hash_oflow(ii) +1 !advance pointer to start of next chain
! end do
! end if
return
666 format(1x, a, '(', i4, ')', 1x, i6)
end subroutine bld_outfld_hash_tbls
!#######################################################################
subroutine bld_htapefld_indices 2,2
!
!-----------------------------------------------------------------------
!
! Purpose: Set history tape field indicies in masterlist for each
! field defined on every tape.
!
! Note: because of restart processing, the actflag field is cleared and
! then set only for active output fields on the different history
! tapes.
!
!-----------------------------------------------------------------------
!
! Arguments:
!
!
! Local.
!
integer :: f
integer :: t
!
! Initialize htapeindx to an invalid value.
!
type(master_entry), pointer :: listentry
do t = 1, ptapes
do f = 1, nflds(t)
listentry => get_entry_by_name
(masterlinkedlist, tape(t)%hlist(f)%field%name)
if(.not.associated(listentry)) then
write(iulog,*) 'BLD_HTAPEFLD_INDICES: something wrong, field not found on masterlist'
write(iulog,*) 'BLD_HTAPEFLD_INDICES: t, f, ff = ', t, f
write(iulog,*) 'BLD_HTAPEFLD_INDICES: tape%name = ', tape(t)%hlist(f)%field%name
call endrun
end if
listentry%act_sometape = .true.
listentry%actflag(t) = .true.
listentry%htapeindx(t) = f
end do
end do
return
end subroutine bld_htapefld_indices
!#######################################################################
logical function hist_fld_active (fname) 44,1
!
!------------------------------------------------------------------------
!
! Purpose: determine if a field is active on any history file
!
!------------------------------------------------------------------------
!
! Arguments
!
character(len=*), intent(in) :: fname ! Field name
!
! Local variables
!
character*(max_fieldname_len) :: fname_loc ! max-char equivalent of fname
integer :: ff ! masterlist index pointer
!-----------------------------------------------------------------------
fname_loc = fname
ff = get_masterlist_indx
(fname_loc)
if ( ff < 0 ) then
hist_fld_active = .false.
else
hist_fld_active = masterlist(ff)%thisentry%act_sometape
end if
end function hist_fld_active
end module cam_history