Adding a result module

A tracer result module is fundamentally a plugin that is run on each tracer at each snapshot, unless there are reasons not to run it. Adding such a plugin means creating a new memory object, new code files, and adding instructions for SPARTA how to use the plugin. These changes constitute a non-trivial modification of the code, so please read the introductory documentation and make sure you understand the code for other tracer results. To add a new result, you need to add the following changes, listed on a per-file basis:

  • build/sparta.h

    • In the section called “TRACER RESULTS”, you will need to add a switch that determines whether your result is written to the output files. This switch should be called OUTPUT_RESULT_XXX where “XXX” stands for the name of your result (which needs to differ from all other results and, ideally, analyses).

    • You may add additional switches for particular fields. For example, if your result contains a field “YYY”, there should be a switch OUTPUT_RESULT_XXX_YYY.

    • Make sure to generously add comments that clearly explain what your result does and what options the user has.

  • src/global.h

    • This file contains the compile-time dependency system used by SPARTA, meaning that it determines which parts of the code are executed. These decisions depend on the outputs chosen by the user in a non-trivial way, since results and analyses can depend on other results and analyses, even if the latter are not written to file. If your results depends on other results or analyses, make sure to add the corresponding decisions.

    • Add your result to the DO_RESULT_ANY switch.

    • In the following section, the lengths of the “stencils” are determined, meaning the number of snapshots that are saved in time for tracers and other data. If your result demands a certain minimum number of snapshots, set the stencil accordingly. Note that increasing the size of stencils can have a very large effect on SPARTA’s memory consumption!

    • In the section titled “Array Indices”, add a three-letter code for your result, similar to IFL for infall or SBK for splashback. This is the index of your result in any result-ordered array.

    • Typically, your result will occupy memory in the form of a dynamical array (see below). Add a memory ID such as MID_DA_RESXXX.

    • If there are any configuration parameters associated with your result, add the defaults using #define.

    • Add the fields to the ConfigData struct.

  • src/io/io_output.c

    • In writeConfig, add your compile-time switches to the output, e.g. PRINT_SETTING(OUTPUT_RESULT_XXX). If your result has config parameters, also add those.

  • src/config.c

    • If your result has no config parameters, you can ignore this file.

    • In setDefaultConfig, set the defaults you created.

    • In readConfigFile, add loops to check for the names of your parameters and set them.

    • In checkConfigUser, check the parameters for erroneous input (e.g., negative values etc).

    • In printConfig, add your parameters to the output.

  • src/global_types.h

    • This unit defines most datatypes used in SPARTA, and your result will be one such datatype. You will need to add a new DynArray type, a forward declaration, and mofidy the union in the DynArray type to also contain your new type.

    • Declare the actual definition of your struct ResultXxx.

    • Add an initializer function addResultXxx. All of these steps are easy to perform by copying the corresponding code parts for an existing result.

  • Add code files src/results/result_xxx.h and src/results/result_xxx.c

    • These files contain the actual code for your tracer result plugin. The easiest way to create them is to copy files from a different result, replace the name everywhere, and delete the content of the routines.

    • Your code files will typically need to contain certain routines, including initConfigResultXxx and outputConfigResultXxx (if there are any config parameters specific to your result), runResultXxx (the routine that is run on each tracer), and outputFieldsResultXxx which sets the output fields that are written to file.

  • src/global_types.c

    • In the ttprops struct initialization, you need to choose how your result acts on tracers in a number of situations (see details below).

    • In rsprops, you need to set the correct values. This struct contains basically all the information SPARTA needs about your result. The three-letter short name should be the same (non-capitalized) name you chose for an index in src/global.h. The DynArray type should of course be the type you created previously. The function pointers can be NULL, in which case no function is executed. For example, if your result has no config parameters, you can set NULL for the initConfig and outputConfig fields. If runResult is NULL, your result will have no effect in that it will never be run on a tracer.

    • In the output fields function, note that you do not need to actually output anything; the output module takes care of that. You only need to tell the output module which fields should be written out and where in your struct it can find them. See existing code for examples. Make sure to respect the switches you created in sparta.h.

    • In printTypeSizes, add the size of your result type to the output.


The compile-time parameters such as DO_RESULT_XXX should be confined to the files and code segments listed above. If they are used anywhere else in the code, that is a sign that your code needs to be structured differently!

One particularly tricky aspect are the instructions listed in global_types.c. These instructions correspond to particular situations when a tracer is born, and your result can respond to these situations with a set of statuses (see src/results/results.h).

For example, you can decide what happens if a tracer is born inside a new halo. In this case, the instruction is INSTR_IN_NEW. If your result does not care whether the infall was tracked, you probably want to set RS_STATUS_SEEKING, i.e., your result is on. If your result cannot be determined in this situation, you might instead set RS_STATUS_FAILED or RS_STATUS_NOT_SEEKING.

Note that you need to set an instruction for each situation and each tracer type, since you may want to treat subhalo and particle tracers differenty.