Commit d25d8a23 authored by Yannick Bahé's avatar Yannick Bahé Committed by Matthieu Schaller
Browse files

Add option for particle type level default output behaviour to new output list scheme

parent d8f091e8
......@@ -68,6 +68,9 @@ CGS. Entries in the file look like:
SmoothingLengths_Gas: on # Co-moving smoothing lengths (FWHM of the kernel) of the particles : a U_L [ cm ]
...
For cosmological simulations, users can optionally add the ``--cosmology`` flag
to generate the field names appropriate for such a run.
Users can select the particle fields to output in snapshot using a (separate)
YAML parameter file. By default, you can define a section `Default` at the
top level of this file (in the exact same way as the file dumped by using the
......@@ -83,9 +86,9 @@ options:
select_output_on: 1
select_output: your_select_output_yaml.yml
This field is mostly used to remove unnecessary output by listing them with
0's. A classic use-case for this feature is a DM-only simulation (pure
n-body) where all particles have the same mass. Outputting the mass field in
This field is mostly used to remove unnecessary output by listing them as
"off". A classic use-case for this feature is a DM-only simulation (pure
N-body) where all particles have the same mass. Outputting the mass field in
the snapshots results in extra i/o time and unnecessary waste of disk space.
The corresponding section of the YAML file would look like:
......@@ -97,6 +100,21 @@ The corresponding section of the YAML file would look like:
Entries can simply be copied from the ``output.yml`` generated by the
``-o`` runtime flag.
For convenience, there is also the option to set a default output status for
all fields of a particular particle type. This can be used, for example, to
skip an entire particle type in certain snapshots (see below for how to define
per-snapshot output policies). This is achieved with the special ``Standard``
field for each particle type:
.. code:: YAML
BlackHolesOnly:
Standard_Gas: off
Standard_DM: off
Standard_DMBackground: off
Standard_Stars: off
Standard_BH: on # Not strictly necessary, on is already the default
Combining Output Lists and Output Selection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
......@@ -1161,8 +1161,7 @@ int main(int argc, char *argv[]) {
/* Verify that the fields to dump actually exist - this must be done after
* space_init so we know whether or not we have gas particles. */
if (myrank == 0)
io_check_output_fields(output_options->select_output, N_total,
with_cosmology);
io_check_output_fields(output_options, N_total, with_cosmology);
/* Say a few nice things about the space we just created. */
if (myrank == 0) {
......
......@@ -884,6 +884,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
const int distributed,
const long long global_counts[swift_type_count],
const long long global_offsets[swift_type_count],
const int num_fields[swift_type_count],
const struct unit_system* internal_units,
const struct unit_system* snapshot_units) {
......@@ -1108,7 +1109,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
H5Gcreate(h_grp, "Counts", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
if (h_grp_counts < 0) error("Error while creating counts sub-group");
if (global_counts[swift_type_gas] > 0) {
if (global_counts[swift_type_gas] > 0 && num_fields[swift_type_gas] > 0) {
io_write_array(h_grp_files, nr_cells, files, INT, "PartType0", "files");
io_write_array(h_grp_offsets, nr_cells, offset_part, LONGLONG,
"PartType0", "offsets");
......@@ -1116,7 +1117,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
"counts");
}
if (global_counts[swift_type_dark_matter] > 0) {
if (global_counts[swift_type_dark_matter] > 0 &&
num_fields[swift_type_dark_matter] > 0) {
io_write_array(h_grp_files, nr_cells, files, INT, "PartType1", "files");
io_write_array(h_grp_offsets, nr_cells, offset_gpart, LONGLONG,
"PartType1", "offsets");
......@@ -1124,7 +1126,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
"counts");
}
if (global_counts[swift_type_dark_matter_background] > 0) {
if (global_counts[swift_type_dark_matter_background] > 0 &&
num_fields[swift_type_dark_matter_background] > 0) {
io_write_array(h_grp_files, nr_cells, files, INT, "PartType2", "files");
io_write_array(h_grp_offsets, nr_cells, offset_background_gpart, LONGLONG,
"PartType2", "offsets");
......@@ -1132,7 +1135,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
"PartType2", "counts");
}
if (global_counts[swift_type_stars] > 0) {
if (global_counts[swift_type_stars] > 0 &&
num_fields[swift_type_stars] > 0) {
io_write_array(h_grp_files, nr_cells, files, INT, "PartType4", "files");
io_write_array(h_grp_offsets, nr_cells, offset_spart, LONGLONG,
"PartType4", "offsets");
......@@ -1140,7 +1144,8 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
"counts");
}
if (global_counts[swift_type_black_hole] > 0) {
if (global_counts[swift_type_black_hole] > 0 &&
num_fields[swift_type_black_hole] > 0) {
io_write_array(h_grp_files, nr_cells, files, INT, "PartType5", "files");
io_write_array(h_grp_offsets, nr_cells, offset_bpart, LONGLONG,
"PartType5", "offsets");
......@@ -2269,152 +2274,168 @@ void io_collect_gparts_background_to_write(
}
/**
* @brief Verify the io parameter file
* @brief Verify that the output selection file is valid.
*
* @param params The #swift_params instance corresponding to the select_output
* file.
* @param N_total The total number of each particle type.
* @param with_cosmolgy Ran with cosmology?
*/
void io_check_output_fields(struct swift_params* params,
void io_check_output_fields(struct output_options* output_options,
const long long N_total[swift_type_count],
int with_cosmology) {
const int with_cosmology) {
/* Loop over each section */
const int MAX_NUM_PTYPE_FIELDS = 100;
/* Parameter struct for the output options */
struct swift_params* params = output_options->select_output;
/* Get all possible outputs per particle type */
int ptype_num_fields_total[swift_type_count] = {0};
struct io_props field_list[swift_type_count][MAX_NUM_PTYPE_FIELDS];
for (int ptype = 0; ptype < swift_type_count; ptype++)
ptype_num_fields_total[ptype] =
get_ptype_fields(ptype, field_list[ptype], with_cosmology);
/* Check for whether we have a `Default` section */
int have_default = 0;
/* Loop over each section, i.e. different class of output */
for (int section_id = 0; section_id < params->sectionCount; section_id++) {
/* Get the name of current (selection) section, without a trailing colon */
char section_name[FIELD_BUFFER_SIZE];
sprintf(section_name, "%s", params->section[section_id].name);
strcpy(section_name, params->section[section_id].name);
section_name[strlen(section_name) - 1] = 0;
/* Is this the `Default` section? */
if (strcmp(section_name, select_output_header_default_name) == 0)
have_default = 1;
/* How many fields should each ptype write by default? */
int ptype_num_fields_to_write[swift_type_count];
/* What is the default writing status for each ptype (on/off)? */
int ptype_default_write_status[swift_type_count];
/* Initialise section-specific writing counters for each particle type.
* If default is 'write', then we start from the total to deduct any fields
* that are switched off. If the default is 'off', we have to start from
* zero and then count upwards for each field that is switched back on. */
for (int ptype = 0; ptype < swift_type_count; ptype++) {
/* Internally also verifies that the default level is allowed */
const enum compression_levels compression_level_current_default =
output_options_get_ptype_default(params, section_name,
(enum part_type)ptype);
if (compression_level_current_default == compression_do_not_write) {
ptype_default_write_status[ptype] = 0;
ptype_num_fields_to_write[ptype] = 0;
} else {
ptype_default_write_status[ptype] = 1;
ptype_num_fields_to_write[ptype] = ptype_num_fields_total[ptype];
}
} /* ends loop over particle types */
/* Loop over each parameter */
for (int param_id = 0; param_id < params->paramCount; param_id++) {
/* Full name of the parameter to check */
const char* param_name = params->data[param_id].name;
char comparison_section_name[FIELD_BUFFER_SIZE];
/* Skip if wrong section */
sprintf(comparison_section_name, "%s", "SelectOutput:");
if (strstr(param_name, comparison_section_name) != NULL) {
/* Check whether the file still contains the old, now inappropriate
* 'SelectOutput' section */
if (strstr(param_name, "SelectOutput:") != NULL) {
error(
"Output selection files no longer require the use of top level "
"SelectOutput; see the documentation for changes.");
continue;
}
/* Skip if top-level section */
sprintf(comparison_section_name, "%s", section_name);
if (strstr(param_name, comparison_section_name) == NULL) continue;
/* Loop over all particle types to check the fields */
int found = 0;
for (int ptype = 0; ptype < swift_type_count; ptype++) {
/* Skip if wrong particle type */
sprintf(comparison_section_name, "_%s", part_type_names[ptype]);
if (strstr(param_name, section_name) == NULL) continue;
int num_fields = 0;
struct io_props list[100];
/* Don't do anything if no particle of this kind */
if (N_total[ptype] == 0) continue;
/* Gather particle fields from the particle structures */
switch (ptype) {
case swift_type_gas:
hydro_write_particles(NULL, NULL, list, &num_fields);
num_fields += chemistry_write_particles(NULL, list + num_fields);
num_fields +=
cooling_write_particles(NULL, NULL, list + num_fields, NULL);
num_fields += tracers_write_particles(NULL, NULL, list + num_fields,
with_cosmology);
num_fields +=
star_formation_write_particles(NULL, NULL, list + num_fields);
num_fields += fof_write_parts(NULL, NULL, list + num_fields);
num_fields +=
velociraptor_write_parts(NULL, NULL, list + num_fields);
break;
case swift_type_dark_matter:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case swift_type_dark_matter_background:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case swift_type_stars:
stars_write_particles(NULL, list, &num_fields, with_cosmology);
num_fields += chemistry_write_sparticles(NULL, list + num_fields);
num_fields += tracers_write_sparticles(NULL, list + num_fields,
with_cosmology);
num_fields +=
star_formation_write_sparticles(NULL, list + num_fields);
num_fields += fof_write_sparts(NULL, list + num_fields);
num_fields += velociraptor_write_sparts(NULL, list + num_fields);
break;
case swift_type_black_hole:
black_holes_write_particles(NULL, list, &num_fields,
with_cosmology);
num_fields += chemistry_write_bparticles(NULL, list + num_fields);
num_fields += fof_write_bparts(NULL, list + num_fields);
num_fields += velociraptor_write_bparts(NULL, list + num_fields);
break;
default:
error("Particle Type %d not yet supported. Aborting", ptype);
}
/* For this particle type, loop over each possible output field */
for (int field_id = 0; field_id < num_fields; field_id++) {
char field_name[PARSER_MAX_LINE_SIZE];
/* Note that section_name includes a : */
sprintf(field_name, "%s%.*s_%s", section_name, FIELD_BUFFER_SIZE,
list[field_id].name, part_type_names[ptype]);
if (strcmp(param_name, field_name) == 0) {
found = 1;
/* Perform a correctness check on the _value_ of that
* parameter */
char field_value[FIELD_BUFFER_SIZE];
parser_get_param_string(params, field_name, &field_value[0]);
int value_is_valid = 0;
for (int allowed_value_index = 0;
allowed_value_index < compression_level_count;
allowed_value_index++) {
if (strcmp(field_value,
compression_level_names[allowed_value_index]) == 0) {
value_is_valid = 1;
break;
}
}
if (value_is_valid) {
/* Found value and it is correct, so move to the next one. */
break;
} else {
error("Choice of output selection parameter %s:%s is invalid.",
field_name, field_value);
}
}
}
/* Skip if the parameter belongs to another output class or is a
* 'Standard' parameter */
if (strstr(param_name, section_name) == NULL) continue;
if (strstr(param_name, ":Standard_") != NULL) continue;
/* Get the particle type for current parameter
* (raises an error if it could not determine it) */
const int param_ptype = get_param_ptype(param_name);
/* Issue a warning if this parameter does not pertain to any of the
* known fields from this ptype. */
int field_id = 0;
char field_name[PARSER_MAX_LINE_SIZE];
for (field_id = 0; field_id < ptype_num_fields_total[param_ptype];
field_id++) {
sprintf(field_name, "%s:%.*s_%s", section_name, FIELD_BUFFER_SIZE,
field_list[param_ptype][field_id].name,
part_type_names[param_ptype]);
if (strcmp(param_name, field_name) == 0) break;
}
if (!found)
if (field_id == ptype_num_fields_total[param_ptype])
message(
"WARNING: Trying to change behaviour of field '%s' (read from "
"'%s') that does not exist. This may because you are not running "
"with all of the physics that you compiled the code with.",
"'%s') that does not exist. This may be because you are not "
"running with all of the physics that you compiled the code with.",
param_name, params->fileName);
/* Perform a correctness check on the _value_ of the parameter */
char field_value[FIELD_BUFFER_SIZE];
parser_get_param_string(params, field_name, field_value);
int value_id = 0;
for (value_id = 0; value_id < compression_level_count; value_id++)
if (strcmp(field_value, compression_level_names[value_id]) == 0) break;
if (value_id == compression_level_count)
error("Choice of output selection parameter %s ('%s') is invalid.",
field_name, field_value);
/* Adjust number of fields to be written for param_ptype, if this field's
* status is different from default */
const int is_on =
strcmp(field_value,
compression_level_names[compression_do_not_write]) != 0;
if (is_on && !ptype_default_write_status[param_ptype]) {
/* Particle should be written even though default is off:
* increase field count */
ptype_num_fields_to_write[param_ptype] += 1;
}
if (!is_on && ptype_default_write_status[param_ptype]) {
/* Particle should not be written, even though default is on:
* decrease field count */
ptype_num_fields_to_write[param_ptype] -= 1;
}
} /* ends loop over parameters */
/* Second loop over ptypes, to write out total number of fields to write */
for (int ptype = 0; ptype < swift_type_count; ptype++) {
#ifdef SWIFT_DEBUG_CHECKS
/* Sanity check: is the number of fields to write non-negative? */
if (ptype_num_fields_to_write[ptype] < 0)
error(
"We seem to have subtracted too many fields for particle "
"type %d in output class %s (total to write is %d)",
ptype, section_name, ptype_num_fields_to_write[ptype]);
#endif
output_options->num_fields_to_write[section_id][ptype] =
ptype_num_fields_to_write[ptype];
}
} /* Ends loop over sections, for different output classes */
/* Add field numbers for (possible) implicit `Default` output class */
if (!have_default) {
const int default_id = output_options->select_output->sectionCount;
for (int ptype = 0; ptype < swift_type_count; ptype++)
output_options->num_fields_to_write[default_id][ptype] =
ptype_num_fields_total[ptype];
}
}
......@@ -2437,57 +2458,8 @@ void io_write_output_field_parameter(const char* filename, int with_cosmology) {
fprintf(file, "Default:\n");
for (int ptype = 0; ptype < swift_type_count; ptype++) {
int num_fields = 0;
struct io_props list[100];
/* Write particle fields from the particle structure */
switch (ptype) {
case swift_type_gas:
hydro_write_particles(NULL, NULL, list, &num_fields);
num_fields += chemistry_write_particles(NULL, list + num_fields);
num_fields +=
cooling_write_particles(NULL, NULL, list + num_fields, NULL);
num_fields += tracers_write_particles(NULL, NULL, list + num_fields,
with_cosmology);
num_fields +=
star_formation_write_particles(NULL, NULL, list + num_fields);
num_fields += fof_write_parts(NULL, NULL, list + num_fields);
num_fields += velociraptor_write_parts(NULL, NULL, list + num_fields);
break;
case swift_type_dark_matter:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case swift_type_dark_matter_background:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case swift_type_stars:
stars_write_particles(NULL, list, &num_fields, with_cosmology);
num_fields += chemistry_write_sparticles(NULL, list + num_fields);
num_fields +=
tracers_write_sparticles(NULL, list + num_fields, with_cosmology);
num_fields += star_formation_write_sparticles(NULL, list + num_fields);
num_fields += fof_write_sparts(NULL, list + num_fields);
num_fields += velociraptor_write_sparts(NULL, list + num_fields);
break;
case swift_type_black_hole:
black_holes_write_particles(NULL, list, &num_fields, with_cosmology);
num_fields += chemistry_write_bparticles(NULL, list + num_fields);
num_fields += fof_write_bparts(NULL, list + num_fields);
num_fields += velociraptor_write_bparts(NULL, list + num_fields);
break;
default:
break;
}
int num_fields = get_ptype_fields(ptype, list, with_cosmology);
if (num_fields == 0) continue;
......@@ -2587,3 +2559,97 @@ void io_get_snapshot_filename(char filename[1024], char xmf_filename[1024],
sprintf(xmf_filename, "%s.xmf", basename);
}
}
/**
* @brief Return the number and names of all output fields of a given ptype.
*
* @param ptype The index of the particle type under consideration.
* @param list An io_props list that will hold the individual fields.
* @param with_cosmology Use cosmological name variant?
*
* @return The total number of fields that can be written for the ptype.
*/
int get_ptype_fields(const int ptype, struct io_props* list,
const int with_cosmology) {
int num_fields = 0;
switch (ptype) {
case swift_type_gas:
hydro_write_particles(NULL, NULL, list, &num_fields);
num_fields += chemistry_write_particles(NULL, list + num_fields);
num_fields +=
cooling_write_particles(NULL, NULL, list + num_fields, NULL);
num_fields += tracers_write_particles(NULL, NULL, list + num_fields,
with_cosmology);
num_fields +=
star_formation_write_particles(NULL, NULL, list + num_fields);
num_fields += fof_write_parts(NULL, NULL, list + num_fields);
num_fields += velociraptor_write_parts(NULL, NULL, list + num_fields);
break;
case swift_type_dark_matter:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case swift_type_dark_matter_background:
darkmatter_write_particles(NULL, list, &num_fields);
num_fields += fof_write_gparts(NULL, list + num_fields);
num_fields += velociraptor_write_gparts(NULL, list + num_fields);
break;
case 3:
break;
case swift_type_stars:
stars_write_particles(NULL, list, &num_fields, with_cosmology);
num_fields += chemistry_write_sparticles(NULL, list + num_fields);
num_fields +=
tracers_write_sparticles(NULL, list + num_fields, with_cosmology);
num_fields += star_formation_write_sparticles(NULL, list + num_fields);
num_fields += fof_write_sparts(NULL, list + num_fields);
num_fields += velociraptor_write_sparts(NULL, list + num_fields);
break;
case swift_type_black_hole:
black_holes_write_particles(NULL, list, &num_fields, with_cosmology);
num_fields += chemistry_write_bparticles(NULL, list + num_fields);
num_fields += fof_write_bparts(NULL, list + num_fields);
num_fields += velociraptor_write_bparts(NULL, list + num_fields);
break;
default:
error("Particle Type %d not yet supported. Aborting", ptype);
}
return num_fields;
}
/**
* @brief Return the particle type code of a select_output parameter
*
* @param name The name of the parameter under consideration.
*
* @return The (integer) particle type of the parameter.
*/
int get_param_ptype(const char* name) {
const int name_len = strlen(name);
for (int ptype = 0; ptype < swift_type_count; ptype++) {
const int ptype_name_len = strlen(part_type_names[ptype]);
if (name_len >= ptype_name_len &&
strcmp(&name[name_len - ptype_name_len], part_type_names[ptype]) == 0)
return ptype;
}
/* If we get here, we could not match the name, so something's gone wrong. */
error("Could not determine the particle type for parameter '%s'.", name);
/* We can never get here, but the compiler may complain if we don't return
* an int after promising to do so... */
return -1;
}
......@@ -21,11 +21,10 @@
#define SWIFT_COMMON_IO_H
/* Config parameters. */
#include "../config.h"
#include "config.h"
/* Local includes. */
#include "part_type.h"
#include "units.h"
#define FIELD_BUFFER_SIZE 64
#define DESCRIPTION_BUFFER_SIZE 512
......@@ -44,6 +43,8 @@ struct xpart;
struct io_props;
struct engine;
struct threadpool;
struct output_options;
struct unit_system;
/**
* @brief The different types of data used in the GADGET IC files.
......@@ -66,6 +67,9 @@ enum IO_DATA_TYPE {
#if defined(HAVE_HDF5)
/* Library header */
#include <hdf5.h>
hid_t io_hdf5_type(enum IO_DATA_TYPE type);
hsize_t io_get_number_element_in_attribute(hid_t attr);
......@@ -104,6 +108,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], const double dim[3],
const int distributed,
const long long global_counts[swift_type_count],
const long long global_offsets[swift_type_count],
const int num_fields[swift_type_count],
const struct unit_system* internal_units,
const struct unit_system* snapshot_units);
......@@ -118,7 +123,7 @@ void io_copy_temp_buffer(void* temp, const struct engine* e,
const struct unit_system* internal_units,
const struct unit_system* snapshot_units);
#endif /* defined HDF5 */
#endif /* HAVE_HDF5 */
size_t io_sizeof_type(enum IO_DATA_TYPE type);
int io_is_double_precision(enum IO_DATA_TYPE type);
......@@ -167,8 +172,9 @@ void io_duplicate_black_holes_gparts(struct threadpool* tp,
struct gpart* const gparts, size_t Nstars,
size_t Ndm);
void io_check_output_fields(struct swift_params* params,
const long long N_total[3], int with_cosmology);
void io_check_output_fields(struct output_options* output_options,
const long long N_total[swift_type_count],
const int with_cosmology);
void io_write_output_field_parameter(const char* filename, int with_cosmology);
......@@ -180,4 +186,8 @@ void io_get_snapshot_filename(char filename[1024], char xmf_filename[1024],
const int stf_count, const int snap_count,