Skip to main content

HYCOM modernization

Goal

Learn how to use Codee to modernize HYCOM, an ocean modeling system consisting of dozens of files.

info

This guide is part of the NERSC + Codee Training Series 2024. Code available for download at the previous link.

Getting started

First, navigate to the source code for HYCOM:

cd codee-demos/Fortran/HYCOM/src

Next, load the latest Codee version available on Perlmutter:

module load codee/2024.3.1

Walkthrough

1. Generate the compile_commands.json

The project comes with a Makefile, so we can leverage the tool bear (version 3 or later) to generate the compile_commands.json file required by Codee:

/global/cfs/cdirs/m4232pub/tools/bin/bear -- make ARCH=codee TYPE=demo

It's a simple as prepending bear -- to the make invocation. This command will produce a compile_commands.json file with all the compiler invocations needed to build the source files.

2. Run the screening report

To explore the recommendations of the Open Catalog that are applicable to HYCOM, let's run Codee's screening report; use --compile-commands to point to the compilation database:

Codee command
codee screening --compile-commands compile_commands.json
Codee output
Note: the compilation database entries will be analyzed in the order necessary to meet module dependencies between Fortran source files.
Configuration file 'compile_commands.json' successfully parsed.
Date: 2024-09-09 Codee version: 2024.3.1 License type: Full

[ 1/50] mod_dimensions.F90 ... Done
[ 2/50] mod_xc.F90 ... Done
<...>
[50/50] hycom.F90 ... Done

SCREENING REPORT

---Number of files---
Total | C C++ Fortran
----- | - --- -------
50 | 1 0 49

Lines of code Analysis time # checks Profiling
------------- ------------- -------- ---------
44679 1 m 2 s 2433 n/a

Lines of code : total lines of code found in the target (computed the same way as the sloccount tool)
Analysis time : time required to analyze the target
# checks : total actionable items (opportunities, recommendations, defects and remarks) detected
Profiling : estimation of overall execution time required by this target

RANKING OF CHECKERS

Checker Priority AutoFix # Title
------- -------- ------- --- --------------------------------------------------------------------------------------------------------------------------------------
PWR068 P27 (L1) 159 Encapsulate external procedures within modules to avoid the risks of calling implicit interfaces
RMK015 P27 (L1) 1 Tune compiler optimization flags to increase the speed of the code
PWR039 P27 (L1) x 1 Consider loop interchange to improve the locality of reference and enable vectorization
PWR008 P18 (L1) x 196 Declare the intent for each procedure parameter
PWR070 P18 (L1) 74 Declare array dummy arguments as assumed-shape arrays
PWR020 P18 (L1) 14 Consider loop fission to enable vectorization
PWR003 P18 (L1) 2 Explicitly declare pure functions
PWR021 P18 (L1) 1 Consider loop fission with scalar to vector promotion to enable vectorization
PWR053 P12 (L1) x 143 Consider applying vectorization to forall loop
PWR054 P12 (L1) x 27 Consider applying vectorization to scalar reduction loop
PWR063 P12 (L1) 19 Avoid using legacy Fortran constructs
PWR060 P12 (L1) 2 Consider loop fission to separate gather memory access pattern
PWR073 P9 (L2) 5 Transform common block into a module for better data encapsulation
PWR024 P8 (L2) 1 Loop can be rewritten in OpenMP canonical form
PWR071 P6 (L2) 831 Prefer real(kind=kind_value) for declaring consistent floating types
PWR007 P6 (L2) x 20 Disable implicit declaration of variables
PWR023 P6 (L2) 1 Add 'restrict' for pointer function parameters to hint the compiler that vectorization is safe
PWR022 P4 (L3) 51 Move invariant conditional out of the loop to facilitate vectorization
PWR034 P4 (L3) 14 Avoid strided array access to improve performance
PWR001 P3 (L3) 188 Declare global variables as function parameters
PWR069 P3 (L3) 180 Use the keyword only to explicitly state what to import from a module
PWR029 P3 (L3) 5 Remove integer increment preventing performance optimization
PWR035 P2 (L3) 214 Avoid non-consecutive array access to improve performance
PWR049 P2 (L3) 45 Move iterator-dependent condition outside of the loop
PWR036 P2 (L3) 36 Avoid indirect array access to improve performance
RMK010 P0 (L3) 192 The vectorization cost model states the loop is not a SIMD opportunity due to strided memory accesses in the loop body
RMK012 P0 (L3) 9 The vectorization cost model states the loop is not a SIMD opportunity because conditional execution renders vectorization inefficient
RMK014 P0 (L3) 2 The vectorization cost model states the loop is not a SIMD opportunity due to unpredictable memory accesses in the loop body

SUGGESTIONS

Use 'roi' to get a return of investment estimation report:
codee roi --compile-commands compile_commands.json

Focus the analysis on a specific file before proceeding with the Codee auto mode or the guided mode:
codee screening specific/file.c --compile-commands compile_commands.json

50 files, 251 functions, 2058 loops successfully analyzed (2433 checkers) and 0 non-analyzed files in 1 m 6 s

3. Run the checks report

Let's focus the analysis on one of the reported checkers. We can filter Codee's output with the --check-id flag. For example, let's pick PWR007, for which Codee has autofix capabilities available.

Codee's checks report will help us identify all places where the checker is applicable:

Codee command
codee checks --compile-commands compile_commands.json --check-id PWR007
Codee output
Note: the compilation database entries will be analyzed in the order necessary to meet module dependencies between Fortran source files.
Configuration file 'compile_commands.json' successfully parsed.
Date: 2024-09-09 Codee version: 2024.3.1 License type: Full

[ 1/50] mod_dimensions.F90 ... Done
[ 2/50] mod_xc.F90 ... Done
<...>
[50/50] hycom.F90 ... Done

CHECKS REPORT

/global/u2/u/user/codee-demos/Fortran/HYCOM/src/machine.F90:4:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:4:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:149:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:355:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:470:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:600:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:651:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:804:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:909:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:955:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1750:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1819:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1887:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1985:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2081:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2152:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2176:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2227:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2258:7 [PWR007] (level: L2): Disable implicit declaration of variables
/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:2305:7 [PWR007] (level: L2): Disable implicit declaration of variables

SUGGESTIONS

Use --verbose to get more details, e.g:
codee checks --verbose --compile-commands compile_commands.json --check-id PWR007

50 files, 251 functions, 2058 loops successfully analyzed (20 checkers) and 0 non-analyzed files in 1 m 1 s

Pick one of the occurrences of PWR007, and re-run the checks report with --verbose to get detailed information. It's important to filter by a specific occurrence (for example, s8gefs.F90:1985), or else the amount of information shown in --verbose mode can easily become overwhelming.

Note: The way bear interacts with Perlmutter's filesystems causes the compilation database to list project files under /global/u2 instead of /global/home. This will prevent Codee from locating the source files when using file filters. To resolve this, we will use the realpath command to adjust the filters.

Codee command
codee checks --compile-commands compile_commands.json --check-id PWR007 --verbose $(realpath s8gefs.F90):1985
Codee output
Note: the compilation database entries will be analyzed in the order necessary to meet module dependencies between Fortran source files.
Configuration file 'compile_commands.json' successfully parsed.
Date: 2024-09-09 Codee version: 2024.3.1 License type: Full

[1/1] s8gefs.F90 ... Done

CHECKS REPORT

/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1985:7 [PWR007] (level: L2): Disable implicit declaration of variables
Suggestion: Add IMPLICIT NONE in the specification part of the procedure 'S8DOT'
Documentation: https://github.com/codee-com/open-catalog/tree/main/Checks/PWR007
AutoFix:
codee rewrite --modernization implicit-none --in-place /global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:S8DOT --compile-commands compile_commands.json

SUGGESTIONS

1 file, 1 function, 4 loops successfully analyzed (1 checker) and 0 non-analyzed files in 3335 ms

4. Autofix

Let's use Codee's autofix capabilities to automatically modernize the code:

Codee command
codee rewrite --modernization implicit-none -o s8gefs_codee.F90 $(realpath s8gefs.F90):S8DOT --compile-commands compile_commands.json
Codee output
Note: the compilation database entries will be analyzed in the order necessary to meet module dependencies between Fortran source files.
Configuration file 'compile_commands.json' successfully parsed.
Date: 2024-09-09 Codee version: 2024.3.1 License type: Full

Results for file '/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90':
Successfully applied AutoFix to the procedure at '/global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1985:7' [using insert implicit none]:
[INFO] Inserted implicit none:
- /global/u2/u/user/codee-demos/Fortran/HYCOM/src/s8gefs.F90:1985:7

Successfully created s8gefs_codee.F90

We can review the code changes to verify correctness:

diff -C 2 s8gefs.F90 s8gefs_codee.F90
  !***END PROLOGUE  S8DOT
!
+ ! Codee: Made all variable declarations explicit (2024-09-09 01:52:17)
+ implicit none
+ integer :: I
+ integer :: INCX
+ integer :: INCY
+ integer :: IX
+ integer :: IY
+ integer :: M
+ integer :: MP1
+ integer :: N
+ integer :: NS
+ real :: S8DOT
REAL SX(*),SY(*)
!***FIRST EXECUTABLE STATEMENT S8DOT