top_left_banner  SimCon logo


WRF - Errors in INTENT Declarations

INTENT Violations in WRF - The Issue

An INTENT (IN) violation occurs when an argument declared INTENT (IN) is written to.

In both WRF version 3.4.1 and version 3.6.1 there are 138 occurrences.

The Fortran INTENT Declaration

The Fortran INTENT declaration specifies that an argument to a subroutine or function is:

(Note that there are other possible intents. An argument may be a sub-program, or it may be a variable which is used to supply attributes such as array bounds but which is neither read no written. No explicit INTENT specification is available for these.)

INTENT Violations and Compiler Error Detection

A simple INTENT (IN) violation is shown below:

SUBROUTINE s (a)
REAL, INTENT (IN) :: a
a=1.0
END SUBROUTINE s

The argument a is declared INTENT (IN) and cannot therefore be written to. We write to it explicitly across an = sign. This is an error and is detected by all compilers which we have tested (gfortran, g95, ifort, ftn95, Compaq Visual Fortran).

INTENT (IN) violations may also be created indirectly, by passing an INTENT (IN) argument down to a second routine which writes to it, for example:

SUBROUTINE s1 (a)
REAL, INTENT (IN) :: a
CALL s2 (a)
END SUBROUTINE s1
 
SUBROUTINE s2 (b)
b=1.0
END SUBROUTINE s2

This error is detected by only one of the compilers which we have tested, ftn95, and even this compiler misses the error if the subroutines s1 and s2 are written within a Fortran module. The error is not detected by either gfortran or ifort which are commonly used to build WRF.

An INTENT (OUT) violation is shown below:

SUBROUTINE s (x,px,v,dt)
REAL, INTENT (OUT) :: x, px
REAL, INTENT (IN) :: v, dt
px = x
x = x + v * dt
END SUBROUTINE s

The variable x is declared INTENT (OUT) but it is copied before it is written to. Clear errors like this do not occur in WRF.

Finding INTENT Errors in WRF

The read and write access to variables is traced by fpt down through sub-program calls. The procedure to assess the number of INTENT errors in WRF is:

  1. If you have not already done so, set up the WRF test environment as described here.
  2. Change to the fpt/ directory and run fpt on WRFV3.6.1_vanilla.fsp or WRFV3.4.1_vanilla.fsp. Specify the argument %i to command fpt to run interactively.
     
    $ cd fpt
    $ fpt WRFV3.6.1_vanilla.fsp %i
  3. The fpt run will pause to show the diagnostics which have been suppressed. Type a carriage return to continue. About 10,000 diagnostics for WRF version 3.6.1 or 6,200 diagnostics for WRF version 3.4.1 will stream past (They are all captured in the list file). The fpt run will pause with an interactive prompt.
  4. Turn off paging. Otherwise fpt will pause after each reported INTENT violation, and there are a lot of them. Run the INTENT check:
     
    FPT> paging off
    FPT> check intent

All of the INTENT (IN) and potential INTENT (OUT) violations will be reported. The report ends with the summary, for example for WRF V3.4.1:

Total number of arguments 55908
Declared INTENT(IN) 28974
Declared INTENT(OUT) 6196
Declared INTENT(INOUT) 5395
No INTENT declaration 15318
Sub-program formal arguments 25
INTENT(IN) Violations 138 0.5%
INTENT(OUT) Violations 0 0.0%
Additional possible INTENT(OUT) Violations 1685 27.2%

The possible INTENT (OUT) violations are situations where the program flow may be such that an INTENT (OUT) argument is read before it is written.

The number of INTENT (IN) violations is a concern. The problem is that the Fortran standard explicitly states that the INTENT specification may be used to control code generation. If an argument is INTENT (IN) the sub-program need not export it. If an argument is INTENT (OUT) a sub-program need not import it. This could be significant in a multi-processor environment where inport or export may be expensive.

Do Compilers Use the INTENT Declarations?

There is a command in fpt to remove almost all of the INTENT declarations in a program. The arguments then default to INTENT (INOUT) and are always imported and exported. A small number of INTENT declarations are required by the language. These occur in the interfaces to overloaded operators and in functions with the attributes PURE and ELEMENTAL. There are a small number of these in WRF (For example, one overloaded operator and one ELEMENTAL routine in WRF V3.4.1). All other INTENT declarations can be removed.

The script tools/WRFV3_fpt_experient.csh has been set up to run an experiment as follows:

  1. The fpt_output directories to which fpt will write re-engineered code are cleaned.
  2. An fpt run is made for a specific engineering change. The re-engineered files are written to the fpt_output directory structure.
  3. The output files are copied to a directory labelled with the name of the experiment so that they may be compared with un-modified files.
  4. The output files are copied to the build directories. A small number of modified Makefiles are also copied to these directories.
  5. The executable wrf.exe is built. The build procedure is almost identical to that distributed by UCAR.
  6. The wrf executable is re-named to label it with the experiment name and is moved to the run_test/WRFV3/main/ directory.
  7. A run of the ideal case em_b_wave is made with the new executable.
  8. The wrfout output file is compared with the file generated by a standard UCAR build.

This process is described in more detail in the wrf_experiments page.

Run the experiment remove_intent:

$ tools/WRFV3_fpt_experiment.csh WRFV3.6.1 remove_intent

      or

$ tools/WRFV3_fpt_experiment.csh WRFV3.4.1 remove_intent

The WRF Fortran files built for this experiment are copied to the directories, e.g. WRFV3.6.1_remove_intent/WRFV3/... . Compare these files with the original UCAR sources, and with WRFV3.6.1_vanilla/WRFV3... or with WRFV3.4.1_vanilla/WRFV3... if you have already made the vanilla (Unchanged) run. We strongly recommend Beyond Compare from Scooter Software to make this comparison. Specify the rule that Fortran comments are delimited by "!" and that comments are unimportant differences.

In our runs, with gfortran and ifort, the wrfout output files are identical to those generated by a standard UCAR build. The compilers tested do not use the INTENT declarations to control code generation. Note that we have just removed about 35,000 INTENT declarations from the code without affecting the behaviour in any way! However, compilers may exist which do make use of INTENT declarations, and if WRF is built with these compilers the results might change.

Enforcing INTENT (IN) as Declared

There is a command in fpt to re-engineer code so as to enforce the declared INTENT (IN) (Please see ENFORCE INTENT (IN) AS DECLARED). Wherever an INTENT (IN) violation is detected fpt declares a new temporary variable with the same attributes as the argument concerned and copies the argument to it. This temporary variable is then used in the code of the sub-program instead of the argument. For example, in the file WRFV3/frame/md_calls.inc , in the subroutine:

SUBROUTINE wrf_put_dom_td_char_arr(datahandle,element,datestr,data,status)
:
CHARACTER*(*),INTENT(IN) :: element

the variable element is written to in the routine ext_ncd_put_dom_td_char. The declaration is therefore added:

CHARACTER*(LEN(element))element_copy

The argument is copied to the temporary:

element_copy = element

and the calls are modified to use the temporary variable, e.g.

CALL ext_ncd_put_dom_td_char(hndl,element_copy,datestr_copy,data, &
status)

To test the effect of enforcing the INTENT (IN) as declared:

  1. Change to the main directory containing fpt/, fpt_output/ and tools/.
  2. Run the experiment enforce_intent_in:
     
    $ tools/WRFV3_fpt_experiment.csh WRFV3.6.1 enforce_intent_in
      or
    $ tools/WRFV3_fpt_experiment.csh WRFV3.4.1 enforce_intent_in

(Please see the notes above on the script WRFV3_fpt_experiment.csh).

In the tests which we have made, the output files generated by running wrf_enforce_intent_in.exe are identical to those produced by a standard UCAR build. The 138 INTENT (IN) violations do not affect the results. However, many more tests are required to be confident that this is always the case.

The Implications for WRF

For the most part, the news is good:

However, the situation is not entirely satisfactory because some compilers may make use of the INTENT declarations. The options are:

  1. To correct the known INTENT errors.
  2. To remove all non-mandatory INTENT declarations.
  3. To corrrect known errors and to use software tools to check for INTENT violations before release.

Option 1 is unsatisfactory because WRF is under continuous development. The large number of violations in the code suggest that the INTENT construct cannot be maintained by hand.

Option 2 removes the risk of compilers mis-interpreting the code, It also removes the risk that maintainers will rely on the INTENT declarations and will inject errors as a result. However, the INTENT declarations are valuable documentation, and compilers may produce code to check them in the future (ftn95 already does so in most cases).

Option 3 depends on the continued availability of tools which can read the WRF source, and this cannot be guaranteed indefinitely.

The Implications For The Fortran Language

Experience of WRF (and of other codes) suggests that the INTENT construct cannot be maintained safely and reliably by hand. A potential problem arises because the Fortran Standard states that INTENT declarations may be used to control code generation. It is propsed that the standard should be changed to state that INTENT declarations may NOT be used to control code generation unless they are checked for correctness by the system. Incorrect INTENT declarations could still mislead maintainers of a code, but this problem may disappear if the checks made by compilers are improved.
 
 

Copyright ©1995 to 2017 Software Validation Ltd. All rights reserved.