Flexible precision in GEOS-Chem

From Geos-chem
Jump to navigation Jump to search

Overview

Flexible precision was introduced in Fortran 90

In most Fortran codes (including GEOS-Chem) you will see declarations such as:

! Integers
INTEGER   :: I, J   ! 4-byte integer  
INTEGER*4 :: K, L   ! 4-byte integer
INTEGER*8 :: M, N   ! 8-byte integer

! Floating point
REAL      :: A, B   ! 4-byte floating point
REAL*4    :: C, D   ! 4-byte floating point
REAL*8    :: E, F   ! 8-byte floating point 

etc. Note that

  1. On most compilers, INTEGER refers to a 4-byte integer. You can make this default to an 8-byte integer by compiling with -i8. In most circumstances it is OK to use 4-byte integers, unless you need to point to a memory location or are reading an 8-byte integer from a netCDF file.
  2. On most compilers, REAL refers to a 4-byte floating-point. You can make this default to an 8-byte floating point by compiling with -r8.
  3. In some older Fortran codes, you will see the term DOUBLE PRECISION. This is the same as REAL*8 -- it is an 8-byte floating point.

Fortran 90 introduced a new precision concept. You can replace these fixed data type with declarations of arbitrary precision. This is done with the SELECTED_REAL_KIND and SELECTED_INT_KIND functions, which are described in more detail here.

--Bob Y. 15:01, 7 November 2014 (EST)

Why are we implementing flexible precision in GEOS-Chem

Long story short: we need to do this in order to interface GEOS-Chem into the NASA GEOS-5 GCM (and other GCM's) more efficiently.

Most of the floating-point variables in GEOS-Chem are declared as REAL*8. (This means that it takes 8 bytes of computer memory to define each number.) We wrote the GEOS-Chem code in this manner, starting many years ago, when we were running on coarser-resolution grids (i.e. 4° x 5°) where memory was not an issue.

Many GCMs—including the GEOS-5 GCM—declare floating-point variables as REAL*4. (A REAL*4 variable only takes 4 bytes of computer memory to store a single number.) Because GCM's typically operate on very fine horizontal grids, conserving memory is a real concern.

When we connect GEOS-Chem to the GEOS-5 GCM, for example, we have to copy REAL*4 data from the GCM (such as the met fields, surface parameters, and relevant quantities) into GEOS-Chem's REAL*8 arrays. This copying operation is very costly, as it requires extra memory and CPU cycles. But if we can transform GEOS-Chem's REAL*8 arrays into REAL*4 arrays, then we could just use point GEOS-Chem's arrays to the GCM arrays without having to do the extra operations associated with the copying process.

Therefore, our goal is to recode GEOS-Chem so that you can pick the floating-point precision that you want to use (either REAL*4 or REAL*8) at compile time. If you are going to connect GEOS-Chem to the GEOS-5 GCM, you can request all of the floating point variables to be declared as REAL*4, in order to match the variables in the GEOS-5 GCM. But if you are using the "traditional" serial GEOS-Chem, you can request that the floating point varaibles be declared as REAL*8, for backwards compatibility with prior code.

--Bob Y. 15:17, 7 November 2014 (EST)

Methodologoy

Here is an example of how you can do this:

1. Define a module named precision_mod.F. You can place this in the Headers directory. This will define a parameter that will be used to specify the precision of variables in other parts of the code.

MODULE PRECISION_MOD

  IMPLICIT NONE
  PRIVATE

#if defined( USE_REAL8 )

  ! Use 8-byte floating point precision when asked for it
  INTEGER, PARAMETER, PUBLIC :: fp = KIND( 0.d0 )

#else

  ! Use 4-byte floating point precision by default
  INTEGER, PARAMETER, PUBLIC :: fp = KIND( 0.0  )

#endif

END MODULE PRECISION_MOD

Instead of having to figure out the proper settings with SELECTED_REAL_KIND, we can just use the KIND command to return that for us. KIND( 0.d0 ) returns the proper "kind" value to define an 8-byte floating point, and KIND( 0.0 ) returns the proper "kind" value for us to define a 4-byte floating point. This value is saved in the constant named fp.

Also note that we have used an #if defined block to define the value of FP. If we compile with -DUSE_REAL8, then the fp can be used to declare 8-byte floating-point variables. Otherwise, fp can be used to declare 4-byte variables by default.

2. In the Makefille_header.mk, we need to add the -DUSE_REAL8 flag to the FFLAGS variable. FFLAGS contains the set of compiler switches and preprocessor statements used to compile GEOS-Chem. This can be done by looking for the following ifeq statement. Add the following lines in RED. This has to be done in both the IFORT section and in the PGI section of Makefile_header.mk

  1. Add include options for ESMF & MAPL

ifeq ($(HPC),yes) INCLUDE += $(MAPL_INC) $(ESMF_MOD) $(ESMF_INC) else FFLAGS += -DUSE_REAL8 endif

3. Add precision_mod.F to the dependencies listing in the Headers/Makefile. Add this line:

precision_mod.o: precision_mod.F90

4. At the top of each GEOS-Chem module or routine (typically in the !USES: comment section), you can place a reference to precision_mod.F. For example, at the top of GeosCore/carbon_mod.F, you would add the line in RED:

!------------------------------------------------------------------------------ ! GEOS-Chem Global Chemical Transport Model  ! !------------------------------------------------------------------------------ !BOP ! ! !MODULE: carbon_mod ! ! !DESCRIPTION: Module CARBON\_MOD contains arrays and routines for performing ! a carbonaceous aerosol simulation. Original code taken from Mian Chin's ! GOCART model and modified accordingly. (rjp, bmy, 4/2/04, 6/30/10) !\\ !\\ ! !INTERFACE: !

     MODULE CARBON_MOD

! ! !USES: ! !

     USE HCO_ERROR_MOD       ! For real precisions (hp)
     USE PRECISION_MOD       ! For GEOS-Chem precision (fp)
     IMPLICIT NONE
     PRIVATE

Note that HEMCO has its own precision parameters. We'll leave those alone, because HEMCO ships as a separate package as well.

5. Look for all variables in the module that are declared as REAL*8. Replace the REAL*8 text with REAL(fp) instead. So these lines:

     REAL*8, ALLOCATABLE :: ANTH_BLKC(:,:,:)
     REAL*8, ALLOCATABLE :: ANTH_ORGC(:,:,:)
     REAL*8, ALLOCATABLE :: BIOB_BLKC(:,:,:)

would become

     REAL(fp), ALLOCATABLE :: ANTH_BLKC(:,:,:)
     REAL(fp), ALLOCATABLE :: ANTH_ORGC(:,:,:)
     REAL(fp), ALLOCATABLE :: BIOB_BLKC(:,:,:)


6. IMPORTANT NOTE! Any literal constants in scientific notation made with the Fortran "d" exponents have to be changed to "e". Also, the text _fp has to be appended to the exponent. This tells Fortran that we are using a customized precision definition. For example, the code:

     ! Molecules OH  per kg OH [molec/kg]
     REAL*8,  PARAMETER  :: XNUMOL_OH  = 6.022d23 / 17d-3
     REAL*8,  PARAMETER  :: CM3PERM3   = 1.d6

would become instead:

     ! Molecules OH  per kg OH [molec/kg]
     REAL(fp),  PARAMETER  :: XNUMOL_OH  = 6.022e+23_fp / 17e-3_fp
     REAL(fp),  PARAMETER  :: CM3PERM3   = 1.e+6_fp

Also, probably for good style, we should use a + sign to denote a positive exponent. The + sign is optional for positive numbers, but it is good style to add it.

7. Repeat the process in steps 4-6 for each GEOS-Chem source code file. It is good practice to modify one module or file at a time, and the run a GEOS-Chem difference test. Then continue.