>>Terms of Use
>>Go BACK to previous page.
This page is intended as a guide for CLM coding standards, practices and conventions. On this page, we focus on five items that we want all developers to concentrate on. Keep these in the back of your mind as you are programming and refer to them if you have a question. We are more than willing to work with you throughout the process (contact us at CLM-CMT@cgd.ucar.edu). If code doesn't generally conform to these items then it will delay the tagging process and we will ask you to modify your code accordingly.
Our past experiences have led us to document a more Comprehensive list of standards that we are striving for. You will find constructs that do not follow these guidelines and we are updating the legacy CLM code base as time and resources permit.
Logical Branches - include a comment after an else or endif statement. Use something that makes sense for the type of logical branch you are using. This is particularly important when there are large numbers of nested blocks. Look at the second if statement below for an even nicer way to accomplish the same thing without comments.
if ( someLogical ) then ! someLogical controls the assignment of x or y x = 0.0_r8 else ! else someLogical y = 0.0_r8 endif ! if someLogical ! Alternative to comments fates_if: if (use_fates) then lch4_if: if (use_lch4) then [...] end if lch4_if end if fates_if |
Pointer Intent - If you pass information to a subroutine via a pointer in an associate block, include a comment as to the intent (OUT, IN, INOUT) of that pointer. This is in addition to a description of that pointer as well as the type and array dimensions. Also see Argument Passing on this page.
associate(& ptrIn => derivedType%readOnlyInRoutine , & ! [character(len=8) (:)] intent = in used for x ptrInOut => derivedType%readAndWriteInRoutine , & ! [real(r8) (:,:,:)] intent = inout used for y ptrOut => derivedType%writeOnlyInRoutine & ! [real(r8) (:)] intent = out used for z ) ... end associate |
Subroutine definition
subroutine BandDiagonal(bounds, lbj, ubj, jtop, jbot, numf, filter, nband, b, r, u) ! ! !DESCRIPTION: ! Tridiagonal matrix solution ! ! !ARGUMENTS: implicit none type(bounds_type), intent(in) :: bounds ! bounds integer , intent(in) :: lbj, ubj ! lbinning and ubing level indices integer , intent(in) :: jtop( bounds%begc: ) ! top level for each column [col] integer , intent(in) :: jbot( bounds%begc: ) ! bottom level for each column [col] integer , intent(in) :: numf ! filter dimension integer , intent(in) :: nband ! band width integer , intent(in) :: filter(:) ! filter real(r8), intent(in) :: b( bounds%begc: , 1: , lbj: ) ! compact band matrix [col, nband, j] real(r8), intent(in) :: r( bounds%begc: , lbj: ) ! "r" rhs of linear system [col, j] real(r8), intent(inout) :: u( bounds%begc: , lbj: ) ! solution [col, j] ! ! ! LOCAL VARIABLES: integer :: j,ci,fc,info,m,n !indices integer :: kl,ku !number of sub/super diagonals integer, allocatable :: ipiv(:) !temporary real(r8),allocatable :: ab(:,:),temp(:,:) !compact storage array real(r8),allocatable :: result(:) !----------------------------------------------------------------------- ! Enforce expected array sizes SHR_ASSERT_ALL((ubound(jtop) == (/bounds%endc/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(jbot) == (/bounds%endc/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(b) == (/bounds%endc, nband, ubj/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(r) == (/bounds%endc, ubj/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(u) == (/bounds%endc, ubj/)), errMsg(__FILE__, __LINE__)) |
Subroutine call
Follow this example in which BandDiagonal is called from SoilTemperatureMod.F90:
call BandDiagonal(bounds, -nlevsno, nlevgrnd, jtop(begc:endc), jbot(begc:endc), & num_nolakec, filter_nolakec, nband, bmatrix(begc:endc, :, :), & rvector(begc:endc, :), tvector(begc:endc, :)) |
RTM example from RtmMod.F90
! ! The call of the routine ! call RtmFloodInit (frivinp_rtm, begr, endr, nt_rtm, & runoff%fthresh( begr:endr ), & evel( begr:endr , : ), & runoff%gindex( begr:endr ), & runoff%lnumr, & flood_active, & effvel_active) ... ! ! The interface definition ! subroutine RtmFloodInit(frivinp, begr, endr, nt_rtm, fthresh, evel, & gindex , & lnumr , & is_rtmflood_on, & is_effvel_on ) ... ! Subroutine arguments ! in mode arguments character(len=*), intent(in) :: frivinp integer , intent(in) :: begr, endr, nt_rtm logical , intent(in) :: is_rtmflood_on !control flooding logical , intent(in) :: is_effvel_on !control eff. velocity integer , intent(in) :: gindex( begr: ) ! global index [begr:endr] integer , intent(in) :: lnumr ! local number of cells ... ! check bounds of arrays SHR_ASSERT_ALL((ubound(fthresh) == (/endr/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(gindex) == (/endr/)), errMsg(__FILE__, __LINE__)) SHR_ASSERT_ALL((ubound(evel) == (/endr, nt_rtm/)), errMsg(__FILE__, __LINE__)) ... |
Pointer - We are moving away from using pointers as a way pass information into routines. We still allow this but with the use of an associate statement.
associate(& ptrIn => derivedType%readOnlyInRoutine , & ! [character(len=8) (:)] intent = in used for x ptrInOut => derivedType%readAndWriteInRoutine , & ! [real(r8) (:,:,:)] intent = inout used for y ptrOut => derivedType%writeOnlyInRoutine & ! [real(r8) (:)] intent = out used for z ) ... end associate |
Global data - We are moving away from using global data as a way pass information into routines. Unless you are extending existing functionality (e.g. using a namelist variable to control some execution) we will generally not allow any new use of a global variable. The other exception to this rule is that it is OK to you use global type declarations.
use clm_varctl , only: iulog, use_cn, use_lch4, use_c13, use_c14, use_cndv ! if ( use_c13 ) then c13o2(p) = forc_pc13o2(g) endif ! use_c13 ! |
use decompMod , only : bounds_type ! using a global type declaration ... subroutine CNSoilLittVertTransp(bounds, num_soilc, filter_soilc) ... type(bounds_type), intent(in) :: bounds ! bounds ... |
Indent of continuation lines: 5 (this can vary based on look and feel)
! a dash, '-', represents a space --use thisModule , only : thisRoutine ! --subroutine foo ----use thatModule , only : thatRoutine ----associate ( & -------ptr1 => someDerivedType%ptr1 , & -------ptr2 => someDerivedType%ptr2 & ----) ----if ( someLogical ) then -------x=x+1 ! leave a line before and after statements that stand alone. -------if (x == 0) then endrun() ! leave a line before and after statements that stand alone. -------do while ( x <= 1000 ) ----------! do some stuff ----------z = 1.0_r8 / radius -------end do ! end x<=1000 ----endif ! someLogical ! ----end associate --end subroutine |
Don't repeat yourself. If you are copying and pasting code, then write a subroutine or function to encapsulate functionality.
! ! an example of using a subprogram to remove repeated code where ! the max daylength is calculated in a function. ! ! From iniTimeConst: ! initialize maximum daylength, based on latitude and maximum declination ! maximum declination hardwired for present-day orbital parameters, ! ± 23.4667 degrees = ± 0.409571 radians, use negative value for S. Hem max_decl = 0.409571 if (grc%lat(g) .lt. 0._r8) max_decl = -max_decl max_dayl(c) = calcMaxDayFunc( grc%lat(g) , max_decl ) ! From CanopyFluxesMod: ! calculate daylength dayl(c) = calcMaxDayFunc( lat(g) , decl(c) ) ! From CNPhenologyMod (copied & pasted in two places in this module): lat = (SHR_CONST_PI/180._r8)*grc%latdeg(pgridcell(p)) dayl(p) = calcMaxDayFunc( lat , decl(c) ) |
! ! an example of what not to do in terms of repeating code. ! The max daylength calculation is repeated. ! ! From iniTimeConst: ! initialize maximum daylength, based on latitude and maximum declination ! maximum declination hardwired for present-day orbital parameters, ! ± 23.4667 degrees = ± 0.409571 radians, use negative value for S. Hem max_decl = 0.409571 if (grc%lat(g) .lt. 0._r8) max_decl = -max_decl temp = -(sin(grc%lat(g))*sin(max_decl))/(cos(grc%lat(g)) * cos(max_decl)) temp = min(1._r8,max(-1._r8,temp)) max_dayl(c) = 2.0_r8 * 13750.9871_r8 * acos(temp) ! From CanopyFluxesMod: ! calculate daylength temp = -(sin(lat(g))*sin(decl(c)))/(cos(lat(g)) * cos(decl(c))) temp = min(1._r8,max(-1._r8,temp)) dayl = 2.0_r8 * 13750.9871_r8 * acos(temp) ! From CNPhenologyMod (copied & pasted in two places in this module): lat = (SHR_CONST_PI/180._r8)*grc%latdeg(pgridcell(p)) temp = -(sin(lat)*sin(decl(c)))/(cos(lat) * cos(decl(c))) temp = min(1._r8,max(-1._r8,temp)) dayl(p) = 2.0_r8 * 13750.9871_r8 * acos(temp) |
Fortran allows you to place multiple statements on one line. Don't do this.
! do this: x = 0 y = 0 z = 0 |
! not this: x = 0 ; y=0; z=0 |
Use temporary variables often
! do this: someFactor = ( const1/const2 ) * const3 additiveFluxes = ( add1Flx + add2Flx + add3Flx ) ** someFactor subFluxes = (sub1Flx + sub2Flx + sub3Flx) ** someFactor sumVal = additiveFluxes + subFluxes |
! not this: sumVal = (( add1Flx + add2Flx + add3Flx ) ** (( const1/const2 ) * const3 )) + ((sub1Flx + sub2Flx + sub3Flx) ** (( const1/const2 ) * const3 )) |
We are in the process of removing CPP Tokens from all of CLM (see our refactoring page ) and will not accept code using them.
! do this: if (useNewPhysics == .true.) then ... ! do some new physics ... endif |
! not this: #if (defined USENEWPHYSICS) ... ! do some new physics ... #endif |