Convert `jules_pftparm` to a duplicate namelist
- Dominant language
- Fortran
- Stars
- 23
- Forks
- 36
- Avg merge
- 5d 9h
- Merged PRs (30d)
- 3
Description
Migrate `jules_pftparm` metadata to jules-shared #41 left an unresolved feature resulting from the implementation of the duplicate namelist functionality in LFRic apps, but not in JULES standalone. The presence of length keyword in the metadata causes the configurator tool to declare the variable as an array, which causes problems when trying to assign the value from `config%jules_pftparm` to `jules_pftparm`. For now `length=:` resides in **jules-um** & **jules-standalone** rather than **jules-shared**. This required an amendment in the parent metadata, for each item for example:
**jules-um** & **jules-standalone**
```
[namelist:jules_pftparm=a_wl_io]
length=:
```
And also amendments in **jules-lfric**
```
[namelist:jules_pftparm=a_wl_io]
fail-if=
!ns=
```
but this was unsatisfactory and a long term solution was required. After discussion, the best solution was to treat the `jules_pftparm` namelist in the same way as LFRic apps i.e. one per PFT, allowing the metadata to be as fully shared as possible. JULES standalone and the UM would also benefit from vastly improved management of the namelist inputs for different surface configurations. See the JULES wiki [Redesigning JULES tile input](https://github.com/MetOffice/jules/wiki/Redesigning-JULES-tile-input) which documents the issues. This issue explores this implementation in JULES standalone.
### ⚠️ Monstrous upgrade macro ⚠️
The upgrade macro has been made more complicated as a result of the need to correct many `jules_pftparm` items entries that are trigger ignored, before applying the macro required for this ticket. This reinforces the need for better management of this namelist. The reasons for these errors and how they have been rectified in this macro are:
1. Upgrade macro was not robust as they only included existing configurations based on `npft = [5, 9, 13]` and have not included a default e.g. setting them all to missing data, which would be picked up by the validator macro or the checking routines in JULES at runtime.
* Added a macro to fix the issue based on the existing macro or set to missing data e.g. `fire_mort_io` (vn5.3_t872), INFERNO (vn4.4_t136), radiation parameters for scaling against observations (no macro)
1. Namelist has been copied from existing configurations with a different number of PFTs and the trigger ignored values have not been updated to the new configuration.
* Where the item was of the form `[x]*npft`, the upgrade macro was robust, an upgrade macro didn't exist or the value was different from the upgrade macro and there weren't too may apps affected in Rose stem, I took a pragmatic approach and corrected these by hand e.g. **rose-stem/app/loobos_jules_es_1p0***; vn5.5_t864, vn5.3_t766, vn4.8_t541
1. Error when applying the upgrade macro, but existing upgrade macro is robust.
* Repeated the existing upgrade macro in this upgrade macro.
The upgrade macro in this issue has been split into two. The first part of the macro (vn8.2_t115a) corrects the `jules_pftparm` namelist and the second (vn8.2_t115) converts the corrected namelist into one namelist per PFT. This is useful as the corrections to the namelist can easily be seen before they are split into separate namelists. These may need to be combined before it is merged to main and is a question for @james-bruten-mo, but it is very useful to have them as separate macros.
### Rose stem tests with `npft=9` with `ncpft=4`
This issue also highlighted the need to be able to identify the surface types present in a configuration when an example upgrade macro was tested so users had an example to follow to write upgrade macros after this issue was resolved. This is documented on the related issue #136, which now blocks this one as the affected Rose stem tests should be corrected before `jules_pftparm` is converted.
### Original `jules_pftparm` method
Before this change the way that `jules_pftparm` items were read into JULES is illustrated here using the standalone method for `a_wl_io` as an example:
[**src/initialisation/standalone/params/init_pftparm.inc**](https://github.com/MetOffice/jules/blob/554b5449f3c7cc1b551c5d0246ac6d72d11a7aa1/src/initialisation/standalone/params/init_pftparm.inc)
```
SUBROUTINE init_pftparm_jules
USE pftparm_io, ONLY: jules_pftparm, init_pftparm_allocated
READ(namelist_unit, NML = jules_pftparm, IOSTAT = ERROR, IOMSG = iomessage)
CALL init_pftparm_allocated()
END SUBROUTINE init_pftparm_jules
```
[**src/science/params/pftparm_io_mod.F90**](https://github.com/MetOffice/jules/blob/554b5449f3c7cc1b551c5d0246ac6d72d11a7aa1/src/science/params/pftparm_io_mod.F90)
```
MODULE pftparm_io
REAL(KIND=real_jlslsm) :: a_wl_io(npft_max) = rmdi
NAMELIST / jules_pftparm/ a_wl_io
CONTAINS
SUBROUTINE init_pftparm_allocated()
USE pftparm, ONLY: a_wl
a_wl(:) = a_wl_io(1:npft)
END SUBROUTINE init_pftparm_allocated
```
[**src/science/params/pftparm_mod.F90**](https://github.com/MetOffice/jules/blob/554b5449f3c7cc1b551c5d0246ac6d72d11a7aa1/src/science/params/pftparm_mod.F90)
```
REAL(KIND=real_jlslsm), ALLOCATABLE :: a_wl(:)
ALLOCATE( a_wl(npft))
a_wl(:) = rmdi
```
Where `USE pftparm, ONLY: a_wl` is the value used in the JULES science code. All JULES tile namelist inputs pretty much follow this design.
### Multiple instance `jules_pftparm` method
There are now multiple instances of `jules_pftparm` which are looped over when the namelist is read in by `read_nml_jules_pftparm`; JULES & UM versions both call the same routine `read_nml_jules_pftparm_instances`. The members of `jules_pftparm` are now scalars rather than arrays. When each instance is read in, the instance is mapped to the correct position in the pftparm allocated arrays (`pftparm_mod`) when `init_pftparm_allocated` is called by the function `map_nml_instance_to_tile_number` . It is therefore the `pftparm_mod` arrays that must be broadcast in the UM rather than `jules_pftparm` namelist when it is read.
**NB. This means that the broadcasting does not happen in the same routine as the reading which breaks the UM Rose stem test that checks for namelist broadcasting.**
```
ERROR: ./jules/src/science/params/pftparm_io_mod.F90 (read_nml_jules_pftparm)
Namelist "jules_pftparm" is read, but has no corresponding broadcast
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Checks completed with 1 failure %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ERROR: Failed files:
* ./jules/src/science/params/pftparm_io_mod.F90
```
[**src/initialisation/standalone/params/init_pftparm.inc**](https://github.com/MetOffice/jules/blob/0f6b8cc735568e35e2085c6aa95e7bad6ae13b9a/src/initialisation/standalone/params/init_pftparm.inc)
```
SUBROUTINE init_pftparm_jules
USE pftparm_io, ONLY: read_nml_jules_pftparm
CALL read_nml_jules_pftparm (nml_dir)
END SUBROUTINE init_pftparm_jules
```
[**src/science/params/pftparm_io_mod.F90**](https://github.com/maggiehendry/jules/blob/0f6b8cc735568e35e2085c6aa95e7bad6ae13b9a/src/science/params/pftparm_io_mod.F90)
```
MODULE pftparm_io
REAL(KIND=real_jlslsm) :: a_wl_io = rmdi
NAMELIST / jules_pftparm/ a_wl_io
CONTAINS
SUBROUTINE read_nml_jules_pftparm(unitnumber)
< Different wrapper depending on UM or JULES >
CALL read_nml_jules_pftparm_instances(unitnumber)
< UM version - Now the allocated arrays are filled, broadcast these to other processors >
END SUBROUTINE read_nml_jules_pftparm
SUBROUTINE read_nml_jules_pftparm_instances(unitnumber)
n = 0
DO ! loop over jules_pftparm instances
READ (UNIT = unitnumber, NML = jules_pftparm, IOSTAT = errorstatus, &
IOMSG = iomessage)
IF (errorstatus == IOSTAT_END) THEN
EXIT
ELSE IF (errorstatus == 0) THEN
! Map jules_pftparm instance to correct position in allocated array
CALL init_pftparm_allocated()
ELSE
! Equivalent to UM check_iostat so both UM & JULES can use this routine
CALL ereport (RoutineName, errorstatus, jules_message)
END IF
END DO
END SUBROUTINE read_nml_jules_pftparm_instances
SUBROUTINE init_pftparm_allocated()
USE pftparm, ONLY: a_wl
USE jules_surface_types_mod, ONLY: map_nml_instance_to_tile_number
i = map_nml_instance_to_tile_number ( 'jules_pftparm', pft_name_io )
a_wl(i) = a_wl_io
END SUBROUTINE init_pftparm_allocated
```
The namelist reading error has been rewritten so that it can be used by either UM or standalone. Outputs the following when error is triggered from standalone.
```
[FATAL ERROR] READ_NML_JULES_PFTPARM_INSTANCES:
Error reading namelist JULES_PFTPARM
IoMsg: Cannot match namelist object name a_wl_io
Please check input list against code.
```
It also checks that the number instances are correct (code snippet not included).
```
[FATAL ERROR] READ_NML_JULES_PFTPARM_INSTANCES: Number of instances of jules_pftparm does not equal npft.
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.