Skip to content

Commit

Permalink
Merge branch 'master' into 2755_psyad_array_notn_bug
Browse files Browse the repository at this point in the history
  • Loading branch information
arporter authored Nov 5, 2024
2 parents 5094dfa + 65e3232 commit f78f838
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 24 deletions.
6 changes: 5 additions & 1 deletion changelog
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,17 @@
88) PR #2753 towards #2717. Adds DataNode.is_character method.

89) PR #2712 for #2711. Adds support for user-supplied Kernels that
operate on dofs (in the LFRic API).
operate on dofs (in the LFRic API).

99) PR #2685 for #2027. Improves datatype and shape inference.

100) PR #2694 for #2690. Extends the PSyData NaN-checking tooling to
perform value verification.

101) PR #2749 for #2311. Alters test-harness generation for the LFRic
API in PSyAD to base the generated test-code filenames on the name of
the supplied LFRic Algorithm file.

release 2.5.0 14th of February 2024

1) PR #2199 for #2189. Fix bugs with missing maps in enter data
Expand Down
4 changes: 2 additions & 2 deletions examples/psyad/eg2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ TL_KERNEL_TYPE = ${TL_KERNEL_NAME}_type
ADJ_KERNEL_TYPE = $(subst tl_,adj_,${TL_KERNEL_TYPE})

# The name of the file that will contain the generated test harness.
HARNESS_X90_FILE = adjoint_test_mod.x90
HARNESS_F90_FILE = adjoint_test_mod.F90
HARNESS_X90_FILE = adjt_hydrostatic_alg_mod.x90
HARNESS_F90_FILE = adjt_hydrostatic_alg_mod.F90

GENERATED_FILES += adj_*.[fF]90 test_harness.exe \
*.x90 ${HARNESS_F90_FILE} psy.f90 *.mod *.o
Expand Down
16 changes: 8 additions & 8 deletions examples/psyad/eg2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ make

This will construct the adjoint of the kernel (written to
`adj_hydrostatic_kernel_mod.x90`) and a test harness in the form of LFRic
Algorithm-layer code (`adjoint_test_mod.x90`). The Makefile then proceeds to
Algorithm-layer code (`adjt_hydrostatic_alg_mod.x90`). The Makefile then proceeds to
use PSyclone to process the test harness Algorithm code to generate algorithm
(`adjoint_test_mod.F90`) and PSy layer (`psy.f90`) code.
(`adjt_hydrostatic_alg_mod.F90`) and PSy layer (`psy.f90`) code.

There is no `compile` target for this example because the generated code
requires the full LFRic infrastructure. However, it is straightforward
Expand All @@ -36,9 +36,9 @@ psyad tl_hydrostatic_kernel_mod.F90 -a r_u exner theta moist_dyn_gas moist_dyn_t
In this case, the adjoint of the tangent-linear kernel is written to
`stdout`.

## Using the generated test harness in the LFRic skeleton mini-app
## Using the generated test harness in the LFRic core skeleton application

These instructions assume that you have a local, compiled version of LFRic
These instructions assume that you have a local, compiled version of LFRic core
in `<lfric-root>` and that the directory containing this file is `<work-dir>`.

1. Create the adjoint kernel and test harness code:
Expand All @@ -51,7 +51,7 @@ make
test-harness code and the TL and adjoint kernels:
```sh
cd <lfric-root>/miniapps/skeleton
cp <work-dir>/adjoint_test_mod.x90 source/algorithm/.
cp <work-dir>/adjt_hydrostatic_alg_mod.x90 source/algorithm/.
cp <work-dir>/tl_hydrostatic_kernel_mod.F90 source/kernel/.
cp <work-dir>/adj_hydrostatic_kernel_mod.F90 source/kernel/.
```
Expand All @@ -61,12 +61,12 @@ cp <work-dir>/adj_hydrostatic_kernel_mod.F90 source/kernel/.
example - if the skeleton mini-app is modified on LFRic trunk then it will
need to be updated):
```sh
sed -e 's/ subroutine run()/ subroutine run()\n use adjoint_test_mod, only: adjoint_test/' -e 's/call skeleton_alg(field_1)/call adjoint_test(mesh, chi, panel_id)/' source/driver/skeleton_driver_mod.f90 > new_driver.f90
sed -e 's/ subroutine run()/ subroutine run()\n use adjt_hydrostatic_alg_mod, only: adjt_hydrostatic_alg/' -e 's/call skeleton_alg(field_1)/call adjt_hydrostatic_alg(mesh, chi, panel_id)/' source/driver/skeleton_driver_mod.f90 > new_driver.f90
mv source/driver/skeleton_driver_mod.f90{,.bak}
mv new_driver.f90 source/driver/skeleton_driver_mod.f90
```

4. Build the modified miniapp:
4. Build the modified application:
```sh
make clean
make
Expand All @@ -75,7 +75,7 @@ make
Note that at this stage you may get errors relating to the LFRic unit-test
framework. These can be ignored.

5. Run the miniapp:
5. Run the application:
```sh
cd example
../bin/skeleton ./configuration.nml
Expand Down
Binary file modified psyclone.pdf
Binary file not shown.
12 changes: 9 additions & 3 deletions src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab
# Modified by J. Henrichs, Bureau of Meteorology
# Modified by L. Turner, Met Office
# Modified by T. Vockerodt, Met Office

''' Provides LFRic-specific PSyclone adjoint test-harness functionality. '''

Expand Down Expand Up @@ -522,11 +523,13 @@ def _lfric_log_write(sym_table, kernel, var1, var2):


def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None,
panel_id_arg_idx=None):
panel_id_arg_idx=None,
test_name="adjoint_test"):
'''
Constructs and returns the PSyIR for a Container and Routine that
implements a test harness for the adjoint of the supplied tangent-linear
kernel.
kernel. The base name to use for the Container and Routine is given by
the test_name argument.
:param tl_psyir: the PSyIR of an LFRic module defining a \
tangent-linear kernel.
Expand All @@ -535,6 +538,8 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None,
field in the list of arguments in the kernel metadata (if present).
:param Optional[int] panel_id_arg_idx: 1-indexed position of the panel-id \
field in the list of arguments in the kernel metadata (if present).
:param Optional[str] test_name: Name of the adjoint test algorithm \
(if present).
:returns: PSyIR of an Algorithm that tests the adjoint of the supplied \
LFRic TL kernel.
Expand All @@ -555,7 +560,8 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None,
f"does not have a Container node:\n{tl_psyir.view(colour=False)}")

lfalg = LFRicAlg()
container = lfalg.create_alg_routine("adjoint_test")
# Variable test_name is validated inside create_alg_routine.
container = lfalg.create_alg_routine(test_name)
routine = container.walk(Routine)[0]
table = routine.symbol_table

Expand Down
24 changes: 22 additions & 2 deletions src/psyclone/psyad/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@
# -----------------------------------------------------------------------------
# Authors: R. W. Ford and A. R. Porter, STFC Daresbury Lab
# Modified by J. Henrichs, Bureau of Meteorology
# T. Vockerodt, Met Office

'''Top-level driver functions for PSyAD : the PSyclone Adjoint
support. Transforms an LFRic tangent linear kernel to its adjoint.
'''
import argparse
import logging
import os
import re
import sys

from psyclone.configuration import Config, LFRIC_API_NAMES
Expand Down Expand Up @@ -97,7 +100,7 @@ def msg():
help='the position of the panel-ID field in the '
'meta_args list of arguments in the kernel metadata '
'(LFRic only)')
parser.add_argument('-otest',
parser.add_argument('-otest', default=None,
help='filename for the unit test (implies -t)',
dest='test_filename')
parser.add_argument('-oad', help='filename for the transformed code')
Expand Down Expand Up @@ -128,6 +131,22 @@ def msg():
"the 'lfric' API.")
sys.exit(1)

# Processing test filename
test_name = "adjoint_test"
if generate_test:
if args.api in LFRIC_API_NAMES:
filename_standard = "adjt_.+_alg_mod.[Xx]90|atlt_.+_alg_mod.[Xx]90"
regex_search = re.search(filename_standard, args.test_filename)
if regex_search is None:
logger.error("Filename '%s' with 'lfric' API "
"must be of the form "
"<path>/adjt_<name>_alg_mod.[Xx]90 or "
"<path>/atlt_<name>_alg_mod.[Xx]90.",
args.test_filename)
sys.exit(1)
# At this stage filename should be valid, so we take the base name
test_name = os.path.basename(args.test_filename).split("_mod.")[0]

# TL Fortran code
filename = args.filename
logger.info("Reading kernel file %s", filename)
Expand All @@ -145,7 +164,8 @@ def msg():
tl_fortran_str, args.active, api=args.api,
coord_arg_index=args.coord_arg,
panel_id_arg_index=args.panel_id_arg,
create_test=generate_test)
create_test=generate_test,
test_name=test_name)
except TangentLinearError as info:
print(str(info.value))
sys.exit(1)
Expand Down
8 changes: 6 additions & 2 deletions src/psyclone/psyad/tl2ad.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab
# Modified by T. Vockerodt, Met Office

'''The implementation of PSyAD : the PSyclone Adjoint
support. Transforms an LFRic tangent linear kernel to its adjoint.
Expand Down Expand Up @@ -66,7 +67,7 @@


def generate_adjoint_str(tl_fortran_str, active_variables,
api=None, create_test=False,
api=None, create_test=False, test_name="adjoint_test",
coord_arg_index=None, panel_id_arg_index=None):
'''Takes a tangent-linear kernel encoded as a string as input
and returns its adjoint encoded as a string along with (if requested)
Expand All @@ -78,6 +79,8 @@ def generate_adjoint_str(tl_fortran_str, active_variables,
:param Optional[str] api: the PSyclone API in use, if any.
:param Optional[bool] create_test: whether or not to create test code for
the adjoint kernel.
:param Optional[str] test_name: base name to use for the file containing
the created adjoint test.
:param Optional[int] coord_arg_index: the (1-based) index of the kernel
argument holding the mesh coordinates (if any). Only applies to the
LFRic API.
Expand Down Expand Up @@ -139,7 +142,8 @@ def generate_adjoint_str(tl_fortran_str, active_variables,
if create_test:
test_psyir = generate_lfric_adjoint_harness(tl_psyir,
coord_arg_index,
panel_id_arg_index)
panel_id_arg_index,
test_name)
else:
raise NotImplementedError(
f"PSyAD only supports generic routines/programs or LFRic "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,11 +572,12 @@ def test_generate_lfric_adjoint_harness(fortran_reader, fortran_writer):
'''Test that the generate_lfric_adjoint_harness() function generates the
expected test-harness code.'''
tl_psyir = fortran_reader.psyir_from_source(TL_CODE)
psyir = generate_lfric_adjoint_harness(tl_psyir)
adjt_name = "adjoint_test_alg"
psyir = generate_lfric_adjoint_harness(tl_psyir, test_name=adjt_name)
gen = fortran_writer(psyir).lower()
assert "use finite_element_config_mod, only : element_order" in gen
assert "module adjoint_test_mod" in gen
assert "subroutine adjoint_test(mesh, chi, panel_id)" in gen
assert "module adjoint_test_alg_mod" in gen
assert "subroutine adjoint_test_alg(mesh, chi, panel_id)" in gen
# We should have a field, a copy of that field and an inner-product value
# for that field.
assert (" real(kind=r_def) :: ascalar\n"
Expand Down
94 changes: 94 additions & 0 deletions src/psyclone/tests/psyad/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
# -----------------------------------------------------------------------------
# Authors: R. W. Ford, A. R. Porter and N. Nobre, STFC Daresbury Lab
# Modified by J. Henrichs, Bureau of Meteorology
# Modified by T. Vockerodt, Met Office

'''A module to perform pytest tests on the code in the main.py file
within the psyad directory.
Expand Down Expand Up @@ -62,6 +63,59 @@
"end module my_mod\n"
)

TEST_LFRIC_KERNEL = '''module tl_foo_kernel_mod
use argument_mod, only : arg_type, func_type, &
GH_FIELD, GH_REAL, &
GH_INC, CELL_COLUMN
use constants_mod, only : r_def, i_def
use fs_continuity_mod, only : W2
use kernel_mod, only : kernel_type
implicit none
private
type, public, extends(kernel_type) :: tl_foo_kernel_type
private
type(arg_type) :: meta_args(1) = (/ &
arg_type(GH_FIELD, GH_REAL, GH_INC, W2) &
/)
integer :: operates_on = CELL_COLUMN
contains
procedure, nopass :: tl_foo_kernel_code
end type
public :: tl_foo_kernel_code
contains
subroutine tl_foo_kernel_code(nlayers, &
field, &
ndf2, undf2, map2 &
)
implicit none
! Arguments
integer(kind=i_def), intent(in) :: nlayers
integer(kind=i_def), intent(in) :: ndf2, undf2
integer(kind=i_def), dimension(ndf2), intent(in) :: map2
real(kind=r_def), dimension(undf2), intent(inout) :: field
! Internal variables
integer(kind=i_def) :: df2, k
do k = 0, nlayers-1
do df2 = 1,ndf2
field( map2(df2)+k ) = 0.0_r_def
end do
end do
end subroutine tl_foo_kernel_code
end module tl_foo_kernel_mod'''

EXPECTED_HARNESS_CODE = '''program adj_test
use my_mod, only : kern
use adj_my_mod, only : adj_kern
Expand Down Expand Up @@ -512,3 +566,43 @@ def test_main_otest_verbose(tmpdir, caplog):
"github actions.")
assert "Writing test harness for adjoint kernel to file" in caplog.text
assert "/harness.f90" in caplog.text


def test_main_otest_lfric(tmpdir, capsys):
''' Test that the -otest option combined with LFRic API
generates the expected adjoint test. '''
filename_in = str(tmpdir.join("tl_foo_kernel_mod.f90"))
filename_out = str(tmpdir.join("atl_foo_kernel_mod.f90"))
harness_out = str(tmpdir.join("atlt_foo_alg_mod.x90"))
with open(filename_in, "w", encoding='utf-8') as my_file:
my_file.write(TEST_LFRIC_KERNEL)
main([filename_in, "-a", "field", "-api", "lfric", "-oad", filename_out,
"-otest", harness_out])
output, error = capsys.readouterr()
assert error == ""
assert output == ""
with open(harness_out, 'r', encoding='utf-8') as my_file:
data = my_file.read()
assert "module atlt_foo_alg_mod" in data.lower()
assert "subroutine atlt_foo_alg" in data.lower()


def test_main_otest_lfric_error_name(capsys, caplog):
''' Test that a bad -otest option combined with LFRic API
generates the expected error. '''
harness_out = "some/path/foo_alg_mod.x90"
logger = logging.getLogger("psyclone.psyad.main")
logger.propagate = True
with caplog.at_level(logging.ERROR, "psyclone.psyad.main"):
with pytest.raises(SystemExit) as err:
main(["input.f90", "-a", "field", "-api", "lfric", "-oad",
"output.f90", "-otest", harness_out])
assert str(err.value) == "1"
x_fail_str = (rf"Filename '{harness_out}' with 'lfric' API "
"must be of the form "
"<path>/adjt_<name>_alg_mod.[Xx]90 or "
"<path>/atlt_<name>_alg_mod.[Xx]90.")
assert x_fail_str in caplog.text
output, error = capsys.readouterr()
assert error == ""
assert output == ""
11 changes: 8 additions & 3 deletions src/psyclone/tests/psyad/tl2ad_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab
# Modified by T. Vockerodt, Met Office

'''A module to perform pytest tests on the code in the tl2ad.py file
within the psyad directory.
Expand Down Expand Up @@ -137,10 +138,14 @@ def test_generate_adjoint_str_lfric_api():
testkern = os.path.join(LFRIC_TEST_FILES_DIR, "tl_testkern_mod.F90")
with open(testkern, mode="r", encoding="utf-8") as kfile:
tl_code = kfile.read()
result, _ = generate_adjoint_str(tl_code,
adj, test = generate_adjoint_str(tl_code,
["xi", "u", "res_dot_product", "curl_u"],
api="lfric")
assert "subroutine adj_testkern_code" in result.lower()
api="lfric",
create_test=True,
test_name="atlt_testkern")
assert "subroutine adj_testkern_code" in adj.lower()
assert "module atlt_testkern_mod" in test.lower()
assert "subroutine atlt_testkern" in test.lower()


def test_generate_adjoint_str_function():
Expand Down

0 comments on commit f78f838

Please sign in to comment.