Custom Deck

For some types of changes to the code it is more convenient to have the end user pass new parameters into the code. This can be for several reasons, and the section on permanent additions to the input deck is given in extensions. At this stage, we will describe how to temporarily add new elements to the input deck parser routines, allowing parameterising of internal and manual initial conditions.

Custom input deck elements are setup in the file src/user_interaction/custom_deck.f90. The function custom_blocks_handle_element is called when a new block is started which the core parser is not familiar with, and once for each element of a block. The function custom_blocks_check is called once the entire deck has been parsed and is used to check that all the elements which are required for the code to run have been specified.

custom_blocks_handle_element

There are three parameters passed to custom_blocks_handle_element, which are:

  • block_name - The name of the block specified in the begin:blockname part of the input deck.
  • element - The name of the element in an input deck element = value pair.
  • value - The string representation of the value in an input deck element = value pair.

custom_blocks_handle_element is first called when a new block is begun using begin:blockname and “blockname” is not recognised by the core input deck parser. The first thing that it does is test whether or not it is a valid custom block. The code does this by passing in the blockname with element and value set to the special constant called “blank”. When extending the input deck, an end user should check if either element or value are set to the special constant “blank”, and if they are then test to see whether the blockname is known or not. If the blockname is known then the code should return the error code c_err_none (No error). If the blockname is not known then the code should return c_err_unknown_block and the deck parser will just skip the block. In operation, this looks like:

FUNCTION custom_blocks_handle_element(block_name, element, value) &
   RESULT(errcode)

 CHARACTER(LEN=string_length), INTENT(IN) :: block_name, element, value
 INTEGER :: errcode

 IF (str_cmp(block_name, "custom")) THEN
   IF (element .EQ. blank .OR. value .EQ. blank) THEN
      ! If element or value are blank then just testing block so
      ! return c_err_none
      errcode = c_err_none
      RETURN
    ENDIF
  ENDIF

 ! The following line must always be present
 errcode = c_err_unknown_block

END FUNCTION custom_blocks_handle_element

In order to simplify Fortran’s rather annoying string handling behaviour, several helper functions have been defined and the most used one is str_cmp(string1, string2). This is a simple routine which returns true if string1 == string2 and false otherwise. It is case sensitive but can deal with differing string lengths etc. The next stage is to deal with the actual element = value pairs in the deck. Each time that a new pair is read from the deck, custom_blocks_handle_element is called with element and value having the values read from the deck. To test for known elements they should just be checked against a known list of names using str_cmp and return the error code c_err_unknown_element if the element isn’t a known element. This looks like:

FUNCTION custom_blocks_handle_element(block_name, element, value) &
   RESULT(errcode)

 CHARACTER(LEN=string_length), INTENT(IN) :: block_name, element, value
 INTEGER :: errcode

 IF (str_cmp(block_name, "custom")) THEN
   IF (element .EQ. blank .OR. value .EQ. blank) THEN
     ! If element or value are blank then just testing block so
     ! return c_err_none
     errcode = c_err_none
     RETURN
   ENDIF
   errcode = c_err_unknown_element
   ! Now test for the real elements
   IF (str_cmp(element, "int_element")) THEN
     errcode = c_err_none
   ENDIF
   IF (str_cmp(element, "real_element")) THEN
     errcode = c_err_none
   ENDIF
   IF (str_cmp(element, "logical_element")) THEN
     errcode = c_err_none
   ENDIF
   RETURN
 ENDIF

 ! The following line must always be present
 errcode = c_err_unknown_block

END FUNCTION custom_blocks_handle_element

This version of the code will allow you to add a new block called “custom” with elements “int_element”, “real_element” and “logical_element” and the code will parse them successfully, while any other block or any other element in the block “custom” will throw errors. However, at this stage the code doesn’t actually read any of the values from the deck. To make it useful, any variable which is read from the input deck must be stored in a global variable. Defining global variables are explained in more detail in the relevant section of the manual, but in short, any variable defined in the module shared_data in the file src/shared_data.F90 will be a global variable. After the variables have been setup, there are once again helper functions to make converting the text from the deck into a normal Fortran90 variable. These helper functions are:

  • as_integer - Attempts to convert a string to an integer. Invokes the maths parser.
  • as_real - Attempts to convert a string to a REAL(num). Invokes the maths parser.
  • as_logical - Attempts to convert a string to a logical. Does not invoke the maths parser (must be either “T” or “F”).

They are used pretty much as expected, except that the return value is passed to the functions so that they can report errors while trying to parse the string. An example would then be:

FUNCTION custom_blocks_handle_element(block_name, element, value) &
   RESULT(errcode)

 CHARACTER(LEN=string_length), INTENT(IN) :: block_name, element, value
 INTEGER :: errcode, int_element
 REAL(num) :: real_element
 LOGICAL :: logical_element

 IF (str_cmp(block_name, "custom")) THEN
   IF (element .EQ. blank .OR. value .EQ. blank) THEN
     ! If element or value are blank then just testing block so
     ! return c_err_none
     errcode = c_err_none
     RETURN
   ENDIF
   errcode = c_err_unknown_element
   ! Now test for the real elements
   IF (str_cmp(element, "int_element")) THEN
     errcode = c_err_none
     int_element = as_integer(value, errcode)
   ENDIF
   IF (str_cmp(element, "real_element")) THEN
     errcode = c_err_none
     real_element = as_real(value, errcode)
   ENDIF
   IF (str_cmp(element, "logical_element")) THEN
     errcode = c_err_none
     logical_element = as_logical(value, errcode)
   ENDIF
   RETURN
 ENDIF

 ! The following line must always be present
 errcode = c_err_unknown_block

END FUNCTION custom_blocks_handle_element

It is possible to perform more advanced types of evaluation of maths expressions such as reading arrays etc. but this is beyond the scope of this manual at present.

custom_blocks_check

This function is called when all the blocks in the input deck have been evaluated and is used to check that all required parameters have been set. If all required elements have been set then you should just return c_err_none, otherwise return c_err_missing_elements. How you test that required elements have been set is up to the developer, and for testing and personal use (which is all that the custom deck parts of the code should be used for) it is acceptable to just not check and always return c_err_none. If permanently expanding the deck, error trapping should always be written.

Previous
Next