Flexible precision in GEOS-Chem: Difference between revisions

From Geos-chem
Jump to navigation Jump to search
No edit summary
Line 39: Line 39:
--[[User:Bmy|Bob Y.]] 15:17, 7 November 2014 (EST)
--[[User:Bmy|Bob Y.]] 15:17, 7 November 2014 (EST)


== Methodologoy ==
== Methodology ==


Here is an example of how you can do this:
We followed this procedure in order to implement flexible precision into GEOS-Chem:


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.
'''1.''' We defined module <tt>Headers/precision_mod.F90</tt>.  This module defines a parameter that will be used to specify the precision of variables in other parts of the code.


  MODULE PRECISION_MOD
  MODULE PRECISION_MOD
Line 64: Line 64:
  END MODULE PRECISION_MOD
  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.
Instead of having to figure out the proper settings with <tt>SELECTED_REAL_KIND<tt>, we can just use the KIND command to return that for us.   


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.
*<tt>KIND( 0.d0 )</tt> returns the proper "kind" value to define an 8-byte floating point
*<tt>KIND( 0.0 )</tt> returns the proper "kind" value to define a 4-byte floating point


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
This value returned by the <tt>KIND</tt> function is saved in the constant named <tt>fp</tt>.


# Add include options for ESMF & MAPL
Note that we have used an <tt>#if defined</tt> block to define the value of FP.  If we compile with <tt>-DUSE_REAL8</tt>, then the <tt>fp</tt> can be used to declare 8-byte floating-point variables.  Otherwise, <tt>fp</tt> can be used to declare 4-byte variables by default.
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:
'''3.''' Add precision_mod.F to the dependencies listing in the Headers/Makefile.  Add this line:


precision_mod.o: precision_mod.F90
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:


!------------------------------------------------------------------------------
'''3.''' In the <tt>Makefille_header.mk</tt>, we added a new Makefile variable named <tt>PRECISION</tt>:
!                  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
# %%%%% Default to 8-byte precision unless specified otherwise %%%%%
      PRIVATE
ifndef PRECISION
  PRECISION    :=8
endif
 
This variable is set to 8 by default (because for now, we want to compile the "traditional" serial GEOS-Chem with <tt>REAL*8</tt> floating point precision, as it has always been compiled. 
 
<tt>PRECISION</tt> is used again further down in the <tt>Makefile_header.mk</tt> to add a C-preprocessor switch:
 
# Add flexible precision declaration
ifeq ($(PRECISION),8)
USER_DEFS      += -DUSE_REAL8
endif
 
The <tt>-DUSE_REAL8</tt> will define the <tt>USE_REAL8</tt> C-preprocessor switch, which in turn will automatically pick 8-byte floating point precision.
 
 
'''4.''' At the top of each GEOS-Chem module or routine (typically in the <tt>!USES:</tt> comment section), you can place a reference to <tt>precision_mod.F90</tt>.  For example, at the top of <tt>GeosCore/carbon_mod.F</tt>, 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.
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:
'''5.''' Look for all variables in the module that are declared as REAL*8.  Replace the REAL*8 text with REAL(fp) instead.  So, in the above example, these lines:


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


would become  
would become  


      REAL(fp), ALLOCATABLE :: ANTH_BLKC(:,:,:)
      REAL(fp), ALLOCATABLE :: ANTH_BLKC(:,:,:)
      REAL(fp), ALLOCATABLE :: ANTH_ORGC(:,:,:)
      REAL(fp), ALLOCATABLE :: ANTH_ORGC(:,:,:)
      REAL(fp), ALLOCATABLE :: BIOB_BLKC(:,:,:)
      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:
'''6.''' IMPORTANT NOTE!  Any literal constants in scientific notation made with the Fortran <tt>d</tt> exponents have to be changed to <tt>e</tt>.  Also, the text <tt>_fp</tt> 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]
      ! Molecules OH  per kg OH [molec/kg]
      REAL*8,  PARAMETER  :: XNUMOL_OH  = 6.022d23 / 17d-3
      REAL*8,  PARAMETER  :: XNUMOL_OH  = 6.022d23 / 17d-3
      REAL*8,  PARAMETER  :: CM3PERM3  = 1.d6
      REAL*8,  PARAMETER  :: CM3PERM3  = 1.d6


would become instead:
would become instead:


      ! Molecules OH  per kg OH [molec/kg]
      ! Molecules OH  per kg OH [molec/kg]
      REAL(fp),  PARAMETER  :: XNUMOL_OH  = 6.022e+23_fp / 17e-3_fp
      REAL(fp),  PARAMETER  :: XNUMOL_OH  = 6.022e+23_fp / 17e-3_fp
      REAL(fp),  PARAMETER  :: CM3PERM3  = 1.e+6_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.''' We repeated the process in steps 4-6 for each GEOS-Chem source code file.  We typically modify a file or two at a time, and then run a difference test.  A difference test compares the code we are editing to a code with known behavior, such as the last accepted benchmarked version.


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.
--[[User:Bmy|Bob Y.]] 15:35, 7 November 2014 (EST)

Revision as of 20:35, 7 November 2014

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. 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. Because GCM's typically operate on very fine horizontal grids, conserving memory is of paramount 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 select 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)

Methodology

We followed this procedure in order to implement flexible precision into GEOS-Chem:

1. We defined module Headers/precision_mod.F90. This module defines 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
  • KIND( 0.0 ) returns the proper "kind" value to define a 4-byte floating point

This value returned by the KIND function is saved in the constant named fp.

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.

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

precision_mod.o: precision_mod.F90

3. In the Makefille_header.mk, we added a new Makefile variable named PRECISION:

# %%%%% Default to 8-byte precision unless specified otherwise %%%%%
ifndef PRECISION
 PRECISION     :=8
endif

This variable is set to 8 by default (because for now, we want to compile the "traditional" serial GEOS-Chem with REAL*8 floating point precision, as it has always been compiled.

PRECISION is used again further down in the Makefile_header.mk to add a C-preprocessor switch:

# Add flexible precision declaration
ifeq ($(PRECISION),8)
USER_DEFS      += -DUSE_REAL8
endif

The -DUSE_REAL8 will define the USE_REAL8 C-preprocessor switch, which in turn will automatically pick 8-byte floating point precision.


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.F90. 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, in the above example, 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


7. We repeated the process in steps 4-6 for each GEOS-Chem source code file. We typically modify a file or two at a time, and then run a difference test. A difference test compares the code we are editing to a code with known behavior, such as the last accepted benchmarked version.

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