Coding Conventions

From LIBISIS
Jump to navigation Jump to search

A good place to start is theUK met office F90 coding guidelines

Object Oriented Design

We will be making use of object oriented concepts to assist with code structure. In this methodology an object and functions which operate on it are bound together in a unit and a select known number of them exposed. In general the object data is not exposed outside of the object itself and must be accessed via functions – this “encapsulation” allows for the internals of the object to be modified later and only a known set of access functions need to be maintained.

With this in mind we will use the FORTRAN90 module as our basic class/object unit. A single module will implement methods for a single data type – in our naming convention type IXTsomething will be defined, along with its methods, in the module IXMsomething contained in file IXMsomething.f90 Though the IXTdata type itself will be public, its internal members should be declared private and thus accessible only via module functions in the IXMsomething module. These modules should be “binding neutral” i.e. they should not refer to MATLAB – separate IXB binding functions will be declared outside of each module to retrieve data to/from the appropriate source. Function that interface between Matlab and FOTRAN will end in the “_m” suffix; other suffices will be defined later as appropriate

Preprocessing

Often you end up writing essentially the same function several times, but changing only one small part (e.g. real for integer or :,: for : ) - one way to get round this is by using a pre-processor. Essentially a pre-processor is “language neutral” and defines a set of text substitutions that should be performed prior to compiling. We are using the C pre-processor as it is called automatically by all the FORTRAN compilers. Substitutions are declared by using a #define command and files for substitutioninserted using #include e.g.

	#define IXD_NAME	testclass
	#include “some_file.f90”

will insert the file “some_file.f90” at the current place in the program, but replacing all instances of IXD_NAME within it with the word “testclass”. We will always prefix variables to be substituted with IXD_ so they are easy to spot. In general we will use

  • IXD_NAME for a derived data type (class) name with the IXT prefix removed
  • IXD_TYPE for a fortran basic data type (real, integer etc)
  • IXD_DIMS for a fortran array dimension specifier (e.g. : or :,: )

Whitespace can get added or removed in funny ways around pre-processor constants – as the Intel compiler does not support concatenation of these tokens, you will see use of the FORTRAN & line continuation character in the included files e.g.

	call IXFfunc_&
 			 IXD_NAME

Will produce, after pre-processing, the line

	Call IXFfunc_&
                                     Testclass

Which is treated by the compiler as

	Call IXFfunc_testclass

Module Declaration and Commenting

A module should be declared

!-----------------------------------------------------------------------------------------------------------------------------------
! MODULE: IXMmodule_name
!-----------------------------------------------------------------------------------------------------------------------------------
!! @author A.N.Author, ISIS
!! @version $Revision: * $ ($Date: * $)
!!
!! Brief description of what the module contains.

@author and @version are F90DOC keywords and any test after a !! will be processed by F90DOC

Subroutine Declaration and Commenting

Subroutines arguments should be arranged in the order:

mandatory [in] (input only) arguments, followed by
mandatory [inout] (input and output) arguments, followed by
mandatory [out] (output) arguments, followed by
optional  [in] (input only) arguments, followed by
optional  [inout] (input and output) arguments, followed by
optional  [out] (output) arguments

These should them be declared in separate blocks and commented as follows

subroutine test(arg1, arg2, arg3, arg4, arg5, arg6)
implicit none
! Mandatory input arguments
integer(i4b) , intent(in) :: arg1 !! description of arg1
! Mandatory input/output arguments
integer(i4b) , intent(inout) :: arg2 !! description of arg2
! Mandatory output arguments
integer(i4b) , intent(out) :: arg3 !! description of arg3
! Optional input arguments
integer(i4b) , intent(in) :: arg4 !! description of arg4
! Optional input/output arguments
integer(i4b) , intent(inout) :: arg5 !! description of arg5
! Optional output arguments
integer(i4b) , intent(out) :: arg6 !! description of arg6

For example

    type(IXTfermi_chopper), intent(in) :: c        !! Fermi chopper
    logical, intent(in) :: sigma       !! sigma = .TRUE. if chopper in phase, =.FALSE. if 180 degrees out of phase
    real(dp), intent(in) :: energy(:)  !! Array of energy values (meV) at which transmission is to be calculated

Nore that we use !! on the variable description so that is it picked up by F90DOC

Separating subroutines and blocks of code within a subroutine

  • Functions and subroutines should be separated from each other by a single line of 120 dashes
  • A comment line that marks a major block of code within subroutines should be underlined by a single line of dashes with the same length as the comment
  • Minor comments should not be underlined
  • Use blank lines to separate portions of code sparingly


Code indentation

Use tabs of 3 characters to indent within modules, the code within a subroutine or function, within if statements etc. For example:

module IXMfermi_chopper
  use IXMtype_definitions
  use IXMbasemodule
      :
contains
  function IXFvariance_gen_fermi_chopper(c, sigma, energy, status)
    implicit none
    ! i/o arguments: [nb: (TGP is pretty certain) array functions must has explicit bounds]
    type(IXTfermi_chopper) :: c			!! Fermi chopper
    logical, intent(in) :: sigma		!! sigma = .TRUE. if chopper in phase, =.FALSE. if 180 degrees out of phase
    real(dp), intent(in) :: energy(:)	!! Array of energy values (meV) at which variance is to be calculated
    type(IXTstatus) :: status			!! Status flag
    real(dp) IXFvariance_gen_fermi_chopper(size(energy))	!! Variance of chopper pulse width (seconds^2)

    ! Get variance:
    if (sigma) then
       sign = 1.0_dp
    else
       sign = -1.0_dp
    endif
    gam = (2.0_dp*(c%radius**2)/c%slit_width)* &
         & abs( 1.0_dp/c%curvature - (sign*fourpi_dp*abs(c%frequency))/sqrt(energy/c_v_to_emev) )
    max_var = (c%slit_width/(fourpi_dp*c%radius))**2 / 6.0_dp