Skip to content

Coding Best Practices

henrique.alves@noaa.gov edited this page Mar 22, 2019 · 2 revisions

A set of guidelines setting coding standards, best practices for upgrading parts of WW3 and for adding new pieces to WW3, regression testing, and maintenance of documentation is essential for community models like WAVEWATCH III™. Below you will find such guidelines, which are an abridged version of the more detailed Developer Guide Booklet, also found in the WW3 repo.

Programming style

WW3 is written in ANSI standard Fortran 90, fully modular, and with an internal dynamic data structure exclusively using use-associated data modules. All modules are internally documented.

WW3 consists of incomplete (.ftn) FORTRAN files that require standard WW3 preprocessing. All changes and additions should be made in these files, not in extracted true FORTRAN files (for details see the manual). The following is expected of codes provided to NCEP for inclusion in the official version of WW3:

  1. Fully document the code following the outline described above.
  2. Follow the coding style of WW3, in particular :
  • For readability, code is written following the use of columns as in fixed format Fortran, even though codes are technically written in free format. Use typical indent strategies for loops and logical structures.
  • Code intended as permanent code is written in upper case, temporary (test) code is written in lower case. Note that we encourage the inclu- sion of permanent test output to be activated at compile time using the WW3 compile switches3 like ‘!/T’. The latter test output should be coded in upper case as a permanent part of the code.
  1. Maintain an update log at the top of each module and for each individual routine or function, and update the last update date in the header of each module, function and routine, as has been done in the distribution version of WW3. If a module only contains one program element, only a single update log needs to be maintained. This is a legacy from code management before using subversion, but will be retained until further notice.
  2. Each subroutine, function or grouping should be embedded in a module to allow for full use association and internal automatic interface checks in Fortran compilers. File naming conventions include:
  • File names for elements of the basic wave model should start with w3.
  • Program elements related to the multi-grid capability should start with wm. Module file names should end in md (before the file extension). • Files with main programs should be stored in file names starting with ww3 . • The file extension.ftn identifies code elements that need to be prepro- cessed by the WW3 preprocessor w3adc to activate switches. • Files with ready-to-use source code (no need for the WW3 prepro- cessor) are identified by the extension .f90. This includes external packages interfaced to WW3. As examples w3snl2md.ftn is a module of the basic wave model (one of the Snl source term options) that needs to be preprocessed by theWW3 prepro- cessor. ww3 grid.ftn contains the main program for the grid preprocessing. mod constants.f90 is a part of a user-supplied package that does not require WW3 code preprocessing. Note that the only file not following the WW3 naming convention is constants.ftn, which contains a module with physical constants.
  1. For now, we have been using the Fortran 90 standard. Required coding practices include:
  • Use free format with style as described above.
  • Use IMPLICIT NONE in each module.
  • Do not use COMMON declarations. Eventually all major data structures should become part of the WW3 dynamical data structures (see man- ual), which are all contained in separate modules, and can be used by use association. See section 3 for suggestions on how to deal with these data structures during (initial) code development.
  • Each module used in a given program element will need to be use associated with a USE statement. Where feasible, use USE module name, ONLY: used names to avoid unintended use of variables in modules.
  • For the same reason, use PRIVATE for general declarations in modules.
  • Declare INTENT on all dummy argument list items.
  • Do not use tab characters in the code (not in Fortran character set).
  • Name ENDs fully both for readability and because several compilers will require this.
  • Do not use numbered DO loops.
  • Use CYCLE and EXIT instead of GOTO. Use CASE statements with a default rather than IF statements for multiple selection tests.
  • As a holdover of days long gone, short variable names have been used throughout the WW3 code. Although this makes it easy to keep docu- mentation readable, it does not necessarily make it easy to understand the code at a glance. Feel free to use longer variable names to make the code more easily understandable.
  • Up to now, there has been no need for explicit KIND declarations in WW3. If such declarations are needed, follow the standard set in Van Delst (2008). vi) Provide documentation for the modules to be included in theWW3 manual. The manual is written in LATEX. Required manual elements to be provided are
  • Description or update of basic equations / physical parameterizations as needed.
  • Description or update of numerical approaches as needed.
  • Update of system documentation including description of parameters in the dynamical data structure of WW3.
  • Document namelist options as applicable in ww3 grid in full in the example input file ww3 grid.inp. The example file is included in full in the manual.

The coding style does not imply that existing packages that are attached to WW3 need to be re-written in this style. However, it is strongly recommended that any such package should be fully documented inside the code. Typically, a user provided pack- age will require an interface routine to WW3. Such an interface routine is expected to conform to the WW3 coding practices.

Note that the NWS claims copyright for all main elements of WW3, and generally will claim copyright for interface routines. Providers of packages to be included with the distribution of WW3 are encourage to provide copyright statements and disclaimers in these packages as appropriate (as NWS will not claim copyright of such packages).

Adding to the model

WW3 is designed as a highly plug-compatible code. Source term and propagation approaches can be included as self-contained modules, with limited changes needed to the interface of routine calls in W3SRCE, W3WAVE, and in the point post-processing programs only. General users can experiment with new approaches in user slots that are provided as dummy model slots like W3SNLX in the file w3snlxmd.ftn for the nonlinear interactions. General users are expected to provide these ‘user slot’ routines to NCEP for inclusion in subsequent versions of WW3, following the instruction in this guide and in the documentation of routines like W3SNLX. Such codes should be self-contained in the way described below.

When providing a module for a source term like W3SNLX or for a propagation scheme the following programming guidelines should be followed:

  1. Follow coding guidelines as outlined in the previous section.
  2. Provide a file with necessary modifications to W3SRCE and all other routines that require modification.
  3. Provide a test case with expected results.

Furthermore, the module needs to be self-contained in the following way.

  1. All saved variables connected with this source term need to be declared in the module header. Upon acceptance as permanent code, they will be converted to the WW3 dynamic data structure.
  2. Provide a separate computation and initialization routine. In the submis- sion, the initialization should be called from the computation routine upon the first call to the routine. Upon acceptance as permanent code, the ini- tialization routine will be moved to a more appropriate location in the code (i.e., being absorbed in ww3 grid or being moved to W3IOGR).

When such packages are provided to NCEP, NCEP may choose to not include the package, or to provide the package as a ‘user slot routine’ like W3SNLX, with some minor work of users required to install these routines, or may choose to fully integrate the routines as a standard option in WW3.

Co-developers of WW3 with access to the subversion server are expected to fully integrate the new modules in the experimental versions of WW3, using software se- lection switches as provided by the NCEP code managers. It is, nevertheless, strongly recommended that initially data structures are kept internal to the modules that are being developed, and that data for the modules are only included in the dynamic data structure of WW3 when the module is mature. This will make code develop- ment and unification much easier when multiple developers are working on the code simultaneously.

The integration of new modules may involve adding new software selection switches to the source code. When this happens it is necessary to also add the new software selection switches to the build scripts. Specifically, the new switches must be prop- erly encoded in model/bin/make file.sh and model/bin/w3 new. This is required so that switch incompatibilities can be trapped at compile time and source files affected by changes to model/bin/switch can be correctly identified and recompiled. Docu- mentation for new software selection switches must be added to Chapter 5.4 of the manual.

The above approach are applicable to inherently modular elements of WW3 such as source terms or propagation schemes. For more intricate changes to the code, please consult the WW3 code managers on how to proceed with developing and providing code upgrades.

Regression testing

Up to model version 3.14, the wave model was distributed with a suite of test cases in the tests directory. This set of tests was inconvenient to use for regression test- ing, as it needed manual intervention and compilation of the code for each individual test. In model version 4.07, an automated script was introduced by Erick Rogers and Tim Campbell of NRL Stennis to convert the traditional tests cases to regression tests that could be run in an automated manner.

The automated regression tests were first maintained in the nrltest directory, and are now kept in the regtests directory. The previous tests directory has been removed, moving the remaining real-world examples in the new cases directory. A more complete docu- mentation of the test cases and of the regression test tools can be found in Section 5.6 in The WAVEWATCH III™ Development Group (WW3DG) (2019).

The regression tests in regtests cover basic tests to assure that features such as propagation and source terms work. The following conventions are used in naming the test cases:

  • ww3 tp1.n: Tests for one-dimensional propagation (regular grids).
  • ww3 tp2.n: Tests for two-dimensional propagation (all grids).
  • ww3 tsn: Tests for source term integration (some including propagation).
  • ww3 btd.n: Tests for wave-bottom interaction. (d = 1 or 2, indicating one- or two-dimensional test cases.)
  • ww3 icd.n: Tests for wave-ice interaction. (d = 1 or 2, indicating one- or two- dimensional test case.)
  • mww3 test nn: Tests cases for the multi-grid wave model.

These idealized test cases should be used for regression testing when new code is added to the repository; i.e., these tests should result in different answers compared to the previous codes only when expected.

More generally, there are two reasons for providing regression tests. The first is the responsibility of a code developer not to break previously existing code. The regression testing gives a developer the tools to check this systematically and rigor- ously. The second is to avoid that a contributor of code has to provide large efforts in keeping the contributed code functional.

By providing a regression testing for your new code, the responsibility of keeping code function will first and foremost fall on developers of new code, not on the contributor of already accepted code. As already outlined above, this implies that developers of new model options should both run relevant regression tests during development, and provide new (options for existing) regression tests for their code additions. Some more details of how and when to regression test will follow below.

To facilitate regression testing, a shell script, regtests/bin/run test is provided along with input files for all test cases. The test cases are organized such that each major test case has its own directory. Within each test case directory, it is possible to have more test cases, for instance by compiling the code with different switches, using different grid configurations, using different model inputs, and running the code in either a shared or a distributed compute environment.

A regression test is run using command-line commands only. All options can be displayed by running the script without arguments or with the -h option

run test

run test -h

the output of which is displayed in Fig. 4.1 at the end of this section.

When editing input files, it is recommended that developers avoid “checking in” trivial changes as updates to the git repository. In the case of ww3 grid.inp and switch, this can easily be avoided by keeping non-version-controlled variants of these files for local editing and accessing them via the -g and -s switches, respectively.

Most WW3 developers will not have reason to edit the run test script itself, i.e. it is treated as a black box. However, developers who would like to extend the capabilities of the script (e.g. by adding new command line switches) and commit these changes to the repository for sharing are encouraged to do so.

With the set of simple test cases, an a large number of switch and grid options, several hundred unique regression tests have been created. A matrix of all possible regression tests can be generated with the tools provided in the regtests/bin directory, as de- scribed in Section 5.6 in The WAVEWATCH III™ Development Group (WW3DG) (2019). The tools also allow for quickly sub-setting the full matrix by test case or other filter options. The NCEP code managers will run the full matrix of regression tests for trunk updates, running the matrix on both the old trunk, and the proposed new trunk, and using the tools in regtests/bin to do bit-wise comparisons of the model results. As it may take up to 12 hours to run the full matrix on our supercomputers, it makes no sense to run the full set of regression tests during development of new options.

During development of code, common sense should prevail. For instance, when developing a new source term package, it is worth while to regularly run some of the source term tests to assure that the other source term options are not broken, and only occasionally run the entire matrix. Note that the NCEP developers do expect that a branch presented for integration with the trunk has been tested at least once with the entire matrix of regression tests by the developer.

There are two basic ways in which the regression testing can be done. One is to keep copy of the trunk from which the development branch is created, and have a set of regression tests run for both the trunk and the development branch, and compare the results with the tools provide. The other is to do the regression testing “in place”.

In this case, when creating a branch, run the selected regression test(s) to create a baseline identifying it with a version identifier. For instance, checking the PR3 and UQ propagation options while working on another propagation scheme, a baseline could be generated with (and possibly other command line options)

./bin/run test -s PR3 -w work PR3 OQ v4.00 ../. ww3 tp1.1

and after modifications are made to the code, the regression test is re-run in a in a different work directory, e.g.,

./bin/run test -s PR3 -w work PR3 UQ v4.01 ../. ww3 tp1.1

after which results can be compared using tools provided or with direct Linux file comparison tools. Note that for a test case to run to completion is a necessary but not sufficient indication of success.

Note that run test has utility beyond regression testing. Perhaps the most useful feature is that it makes it possible to fully document (or communicate) a model simu- lation using a single line of text, “./bin/run test ...” without ambiguity. Furthermore, the utility allows for direct comparison of options in the model. For instance, the GSE test can be used to easily generate GrADS output for all different propagation schemes on a regular grid with otherwise identical model settings.

Manual and documentation

The WW3 manual and other WW3 documents like this guide are written in LATEX. Since these are dynamic documents, the corresponding files are maintained in git, together with the WW3 source code, script and auxiliary files. Because the manual is rather large, it has been stored in several .tex files. During the development of model version 4.18, most sub-sections were placed in their own file, to minimize conflicts in editing when many contributors work on the manual simutaneously. The main files (and directories) making up the manual are

manual.tex Main .tex file, mainly combining the .tex files below into the complete manual.

defs.tex User defined LATEX constructs used in the manual.

start.tex Title page and table of contents set up.

intro.tex Chapter: Introduction, using directory intro.

eqs.tex Chapter: Governing equations, using directory eqs.

num.tex Chapter: Numerics, using directory num.

run.tex Chapter: Running the model, using directory run.

impl.tex Chapter: Installing the model, using directory impl.

sys.tex Chapter: System documentation, using directory sys.

app.tex Appendices, using directory app.

manual.bib BibTex database with references used in the manual.

jas.bst Bibliography style file used for the manual.

All files for the main chapters and appendices manage individual LATEX files for indi- vidual sections (appendices) in the chapter. This way, new options are documented in their own sub-file, and can therefore be easily managed. See the main chapter files for the contents of the chapter directories. Note that the chapter directories also include files for many individual figures.

A makefile is provided to compile the manual. The default make target will compile a PDF version of the manual (manual.pdf). Other make targets available are: ”inp” (create LATEXversions in current directory of the ww3 *.inp and gx *.inp input files), as well as outputs of the ww3 gspl.sh and run test scripts, ”clean” (remove all files created during compile of manual). The following external programs are required and must be found in the user PATH: latex, bibtex and dvipdf.

The example input files (ww3 *.tex and gx *.tex) required for the programs described in run.tex are automatically generated during compilation of the manual. The source input files are copied from $(INPDIR). The default setting for INPDIR, ”../inp”, can be overridden by setting INPDIR either on the make command-line or in the environment. This method assures that the example input files provided with the code are the files displayed in the manual.

Note that the manual consist of both a conventional manual and a basic system doc- umentation. The following standards should be used in writing LATEX contributions to the manual:

  • Use American spelling and grammar.
  • Use dynamic references to equation, chapter and section numbers, etc. Do not use any hardwired reference numbers when referring to equations, sections etc.
  • Use BibTex exclusively for references to other work. Do not write any references directly into the text.
  • Do not use excessive line lengths in the .tex files. We typically use a maximum line length of 78 characters and ‘auto-fill-mode’ when writing or updating .tex files using emacs.
  • When adding contributions to the manual, add a note of the update to the introduction, so that users of the public releases have a concise log of upgrades since the previous model release.
  • If you have no LATEX capability or experience, contact the WW3 code managers to determine an acceptable method of delivering contributions to the manual.

For general users we will provide a recent manual package when they are ready to provide their manual contributions. For co-developers, the most recent version of the manual will be available on github.