Adding Outputs

Adding a derived variable

Derived variables are variables which are defined on the Cartesian spatial grid but are not directly updated by the solver. They are calculated when needed for output or for use in other physics packages. The final form of a derived variable is an array on each processor with the same size as the field arrays. Examples include number_density, ekbar and poynting_flux.

A derived variable is defined on the same grid as the main simulation variables and must be written to disk in such a way as to stitch the parts of the grid from each processor together. This is achieved using the routine:

CALL sdf_write_plain_variable(sdf_handle, id, name, units, dims, stagger, &
   grid_id, variable, subtype_field, subarray_field)

The parameters have the following types and meanings:

  • block_id - The id name of the variable. This character string is a unique identifier for the variable in the file enabling a program to retrieve it later. Once defined it should not change so that newer versions of EPOCH can still identify variables generated by older versions.
  • name - The display name of the variable. This character string is the name that is used by external programs to display an identifying name for the variable. If it contains ‘/’ characters then these are used by VisIt to group the variables.
  • units - The units of the variable. This character string is used when displaying the data units. For most variables in EPOCH these are SI units.
  • dims - An nD integer array containing the GLOBAL length of the variable across all processors. In EPOCH a variable actually called “dims” exists for variables which are the same size as the default field variables.
  • stagger - An integer constant containing the stagger of a variable from the cell centre of a cell. This property lets external programs know the position of a variable on the grid.
  • grid_id - The id name of the grid to which the variable is attached. In EPOCH, the main grid is just called “grid”. Note that this property is case sensitive.
  • variable - The actual variable to be written to disk.
  • subtype_field - This is an MPI type representing the layout of the data across the processors. For a standard field variable, there is an automatically created type called “subtype_field” which should be used here.
  • subarray_field - This is an MPI type representing the section of the “variable” parameter to be written. For a standard field variable, there is an automatically created type called “subarray_field” which should be used here.

It’s probably easiest to read the diagnostics.F90 file and see how the code implements the output of simple variables like ex or ey for an example of how this works. Once the appropriate sdf_write call has been added to the code, there is no further work to be done. The IDL, MatLab and VisIt routines will all read the existence of the variable from the metadata in the SDF file, and it will now be available to view in all SDF reading packages.

There is a working variable called array which is large enough to store a derived variable. It is therefore recommended that to calculate derived variables a new subroutine should be created which populates array with the required variable and then writes it to disk. An example would look like:

IF (IAND(dumpmask(c_dump_myvar), code)) THEN
 CALL calc_my_variable(array)

 CALL sdf_write_plain_variable(sdf_handle, 'my_var', 'Mine/variable', 'unit',
     dims, c_stagger_cell_centre, 'grid', array, subtype_field, subarray_field)
ENDIF

where calc_my_variable is a function which calculates the variable which you wish to write. The form of this function depends on the type of variable to be calculated and is given in the next section.

Adding a particle variable

The next simplest type of output to add is a new property for all particles. To add new particle variables to the output dump, two things are needed: a call to the SDF command to write the data and an iterator function to iterate through all the particles. NOTE: This section only deals with new outputs for existing particle variables. Creation of a new particle variable is more involved, and requires modifying MPI routines.

The iterators are stored in the file iterators.F90. An example iterator is:

 ! iterator for particle momenta
 FUNCTION iterate_px(array, n_points, start)

   REAL(num) :: iterate_px
   REAL(num), DIMENSION(:), INTENT(OUT) :: array
   INTEGER, INTENT(INOUT) :: n_points
   LOGICAL, INTENT(IN) :: start
   TYPE(particle), POINTER, SAVE :: cur
   TYPE(particle_list), POINTER, SAVE :: current_list
   INTEGER :: part_count

   IF (start)  THEN
     CALL start_particle_list(current_species, current_list, cur)
   ENDIF

   part_count = 0
   DO WHILE (ASSOCIATED(current_list) .AND. (part_count .LT. n_points))
     DO WHILE (ASSOCIATED(cur) .AND. (part_count .LT. n_points))
       part_count = part_count + 1
       array(part_count) = cur%part_p(1)
       cur=>cur%next
     ENDDO
     ! If the current partlist is exhausted, switch to the next one
     IF (.NOT. ASSOCIATED(cur)) CALL advance_particle_list(current_list, cur)
   ENDDO
   n_points = part_count

   iterate_px = 0

 END FUNCTION iterate_px

This is a fairly complicated routine which includes code for dealing with the possibility of particle species not being dumped, and other complicated book keeping. Luckily, there is only one line in the routine which needs to change to output a new variable. This being:

       array(part_count) = cur%part_p(1)

To write a new iterator, you just have to copy the skeleton of an existing iterator and change this line to copy your particle property into the “array” array. The details of the particle structure’s contents is explained here. Once your new iterator has been written and added into the iterators.F90 file, it’s time to add the SDF routine to actually write the data. The routine is:

   CALL write_particle_variable(c_dump_id, code, name, iterator)

The parameters this time are

  • c_dump_id - The index into the dumpmask for this variable.
  • code - The dump code for the current output dump.
  • name - The display name to use for this variable.
  • iterator - The name of the iterator function that you created in the previous step. Note that this is not a string but simply the name of the function.

Once again, looking at how this is implemented for one of the existing variables (e.g. px) is probably the most enlightening way to see how it works. As for the fluid variables, the new variable will appear in IDL, MatLab and VisIt.

At this point it is possible to write any property which is similar to the default field variables or the default particle properties. It becomes slightly more challenging if you want to write other types of variable into an output file.

Next