There are two classes of model fields in the UM. Primary fields are things that get evolved by the model, and are updated every timestep. Diagnostic fields are calculated from the primary fields, and don't require an evolution equation. Diagnostic fields are only calculated when they are requested to be output by the stash system.

New sections will generally have three sections - initialisation, timestepping and finalisation. In the initialisation step the model fields are set up, with calls to the STASH system to allocate space for the variables. The timestepping step updates the field values for the next timestep and the finalisation step cleans up any allocated space. Once the fields are registered with the STASH system I/O is handled automatically, including reading the initial fields and writing dump files.

Component Setup


It is helpful to have all code related to the new section as part of a module. This keeps everything self-contained & minimises time spent searching through the code. For this explaination we'll call the module foo, for your code you should use a meaningful name that tells people what the code's supposed to do.

module foo
implicit none
 private
 public init, timestep, finalise
contains
 subroutine init
 end subroutine
 
 subroutine timestep
 end subroutine
 
 subroutine finalise
 end subroutine
end module

You'll need to add calls to your module functions from the model code. The top level UM code is in the routine U_MODEL (src/control/top_level/u_model.F90), this calls ATM_STEP (src/control/top_level/atm_step.F90) in a loop to do the timestepping.

At the top of U_MODEL, just after use atm_fields_mod, load your module:
use foo, only foo_init => init, foo_timestep => timestep, foo_finalise => finalise
While generic names are fine within the module they may conflict with the names of other functions in the main code. The => syntax changes the name of the functions, adding a prefix foo_ to ensure the names are unique.

Add a call to foo_init before the start of the timestep loop (after the call to INITIAL is a good place). At the moment this isn't taking any arguments, we'll add those later. If any cleanup is needed add a call to foo_finalise before ICCLOSEOUTPUT.

In order for the stepping routines to access the STASH systems they must be called from within the ATM_STEP routine. The stash system divides fields into sections, then gives each field in a section a code to identify it. Within ATM_STEP the data sent to STASH gets put into 'stashwork' arrays, there is a separate one for each section (stashwork1 to stashwork30). Section 0 is special, it holds the primary fields and is written out elsewhere.

If you're writing out diagnostic fields then you'll need to call your timestep routine between where the stashwork array for that section is ALLOCATEd and the call to STASH for that array. Depending on what your code is doing it may be sensible to put it into one of the subroutines called by ATM_STEP, or it may make sense to have it at the top level. Keep in mind how updating your fields will affect other calculations.

Adding Primary Fields


The UM stores all of the primary fields in a single array called D1. Helper pointers to the default fields are supplied in the module atm_fields_mod, for your own fields you should add helper pointers to your own module so that other components can make use of it. Storage for the data is automatically set up by the STASH system, primary fields are in STASH section 0. A single level field with a halo would look like:
module foo
implicit none
 private
 public init, timestep, finalise
 
 ! The primary field is public so other modules can use it
 real, public, pointer, dimension(:,:,:) :: field
 integer, parameter :: model = 0
 integer, parameter :: section = 0
 integer, parameter :: fieldstash = 123
contains
 subroutine init(stashindices, stashdata, row_length, rows, offx, offy)
  use d1_chk_mod
  implicit none
  real, dimension(:,:,:), intent(in)     :: stashindices
  real, dimension(:), target, intent(in) :: stashdata
  integer, intent(in) :: row_length, rows, offx, offy
  integer :: data_start, data_end
  integer :: level_size, level_count
 
  level_count = 1
  level_size  = (row_length + 2*offx)*(rows + 2*offy)
  data_start = stashindices(fieldstash, section, model)
  data_end   = data_start + level_size * level_count
 
  ! Set the pointer to point to D1 with the correct shape
  field(1-offx:row_length+offx,1-offy:rows+offy,1:level_count) => &
      stashdata(data_start:data_end)
 
  ! Verify that no other variable is using this area, the fieldname is only used in error messages
  call d1_chk_add_ref("Fieldname", data_start, data_end)
 end subroutine
 ...
end module
and the initialisation would be called like
call foo_init(SI,D1,row_length,rows,offx,offy)

Update the field

Adding Diagnostic Fields


Unlike primary fields diagnostic fields are not stored across timesteps. Instead they are written out only if requested by STASH, check the SF array to see what has been requested.

module foo
implicit none
 private
 public init, timestep, finalise
 
 integer, parameter :: model = 0
 integer, parameter :: section = 1
 integer, parameter :: fieldstash = 123
contains
 ...
 subroutine timestep(stashwork, stashflag, stashindex, row_length, rows, at_extremity, &
                     stlist, stash_levels)
  implicit none
  real,    intent(in) :: stashwork(:)
  logical, intent(in) :: stashflag(:,:)
  integer, intent(in) :: stashindex(:,:,:)
  integer, intent(in) :: row_length, rows
  logical, intent(in) :: at_extremity(4)
  integer, intent(in) :: stlist(:), stash_levels(:,:)
  real                :: field(1:row_length, 1:rows, 1)
  integer             :: icode
  character(len=80)   :: cmessage
 
  ! Check if diagnostic was requested
  if (stashflag(fieldstash,section)) then
    ! Calculate the field
    call calculate_diagnostic(field)
 
    ! Write out the field
    call copydiag_3d(stashwork(stashindex(fieldstash, section, model)), &
                     field, size(field,1), size(field,2), size(field,3), &
                     0,0,0,0,at_extremity,stlist, size(stlist), &
                     stash_levels, size(stash_levels,1), &
                     model, section, fieldstash, icode, cmessage)
    if (icode .ne. 0) then
     ! Handle errors
     call ereport("Foo step", icode, cmessage)
    end if
  end if
 end subroutine
 
 subroutine calculate_diagnositic(field)
  ...
 end subroutine
 ...
end module

Stashmaster Files


In order for the STASH system to know about new fields you need to create a STASHMaster file that describes them. The STASHMaster file contains multiple records in the format
1|Model |Sectn | Item |Name                               |
2|Space |Point | Time | Grid |LevelT|LevelF|LevelL|PseudT|PseudF|PseudL|LevCom|
3| Option Codes                   | Version Mask         | Halo |
4|DataT |DumpP | PC1  PC2  PC3  PC4  PC5  PC6  PC7  PC8  PC9  PCA |
5|Rotate| PPFC | USER | LBVC | BLEV | TLEV |RBLEVV| CFLL | CFFF |
followed by an end of file marker.

The full format of the file is described in the UM documentation. In brief the first line gives the field name & stash code, the second defines where the field is stored, what times it is available and how big it is. The third line contains more availablility information, what UM versions use the field and the size of the field halo. Line 4 describes data backing & the last line details PP format specific information.

Examples


To be added