/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* This object's header. */
#include "common_io.h"
/* Local includes. */
#include "error.h"
#include "io_properties.h"
#include "output_options.h"
#include "units.h"
/* Some standard headers. */
#include
/**
* @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 io_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;
}
/**
* @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?
* @param with_fof Include FoF related fields?
* @param with_stf Include STF related fields?
*
* @return The total number of fields that can be written for the ptype.
*/
int io_get_ptype_fields(const int ptype, struct io_props* list,
const int with_cosmology, const int with_fof,
const int with_stf) {
int num_fields = 0;
switch (ptype) {
case swift_type_gas:
io_select_hydro_fields(NULL, NULL, with_cosmology, /*with_cooling=*/1,
/*with_temperature=*/1, with_fof, with_stf,
/*with_rt=*/1, /*e=*/NULL, &num_fields, list);
break;
case swift_type_dark_matter:
case swift_type_dark_matter_background:
io_select_dm_fields(NULL, NULL, with_fof, with_stf, /*e=*/NULL,
&num_fields, list);
break;
case swift_type_neutrino:
io_select_neutrino_fields(NULL, NULL, with_fof, with_stf, /*e=*/NULL,
&num_fields, list);
break;
case swift_type_stars:
io_select_star_fields(NULL, with_cosmology, with_fof, with_stf,
/*with_rt=*/1, /*e=*/NULL, &num_fields, list);
break;
case swift_type_sink:
io_select_sink_fields(NULL, with_cosmology, with_fof, with_stf,
/*e=*/NULL, &num_fields, list);
break;
case swift_type_black_hole:
io_select_bh_fields(NULL, with_cosmology, with_fof, with_stf, /*e=*/NULL,
&num_fields, list);
break;
default:
error("Particle Type %d not yet supported. Aborting", ptype);
}
return num_fields;
}
/**
* @brief Prepare the output option fields according to the user's choices and
* verify that they are valid.
*
* @param output_options The #output_options for this run
* @param with_cosmology Ran with cosmology?
* @param with_fof Are we running with on-the-fly Fof?
* @param with_stf Are we running with on-the-fly structure finder?
* @param verbose The verbose level
*/
void io_prepare_output_fields(struct output_options* output_options,
const int with_cosmology, const int with_fof,
const int with_stf, int verbose) {
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] = io_get_ptype_fields(
ptype, field_list[ptype], with_cosmology, with_fof, with_stf);
/* 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];
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] = {0};
/* What is the default writing status for each ptype (on/off)? */
int ptype_default_write_status[swift_type_count] = {0};
/* Default snapshot basename for this output selection */
char basename[FILENAME_BUFFER_SIZE] = select_output_default_basename;
/* Default snapshot subdir name for this output selection */
char subdir_name[FILENAME_BUFFER_SIZE] = select_output_default_subdir_name;
int subsample[swift_type_count];
for (int i = 0; i < swift_type_count; ++i)
subsample[i] = select_output_default_subsample;
float subsample_fraction[swift_type_count];
for (int i = 0; i < swift_type_count; ++i)
subsample_fraction[i] = select_output_default_subsample_fraction;
/* 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 lossy_compression_schemes compression_level_current_default =
output_options_get_ptype_default_compression(
params, section_name, (enum part_type)ptype, verbose);
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;
/* 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.");
}
/* 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;
/* Deal with a possible non-standard snapshot basename */
if (strstr(param_name, ":basename") != NULL) {
parser_get_param_string(params, param_name, basename);
continue;
}
/* Deal with a possible non-standard snapshot subdir name */
if (strstr(param_name, ":subdir") != NULL) {
parser_get_param_string(params, param_name, subdir_name);
continue;
}
/* Deal with a possible non-standard subsampling option */
if (strstr(param_name, ":subsample") != NULL) {
parser_get_param_int_array(params, param_name, swift_type_count,
subsample);
continue;
}
/* Deal with a possible non-standard subsampling fraction */
if (strstr(param_name, ":subsample_fraction") != NULL) {
parser_get_param_float_array(params, param_name, swift_type_count,
subsample_fraction);
continue;
}
/* Get the particle type for current parameter
* (raises an error if it could not determine it) */
const int param_ptype = io_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;
}
int param_is_known = 0; /* Update below if it is a known one */
if (field_id < ptype_num_fields_total[param_ptype])
param_is_known = 1;
else
message(
"WARNING: Trying to change behaviour of field '%s' (read from "
"'%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 param_value[FIELD_BUFFER_SIZE];
parser_get_param_string(params, param_name, param_value);
int value_id = 0;
for (value_id = 0; value_id < compression_level_count; value_id++)
if (strcmp(param_value, lossy_compression_schemes_names[value_id]) == 0)
break;
if (value_id == compression_level_count)
error("Choice of output selection parameter %s ('%s') is invalid.",
param_name, param_value);
/* Adjust number of fields to be written for param_ptype, if this field's
* status is different from default and it is a known one. */
if (param_is_known) {
const int is_on =
strcmp(param_value,
lossy_compression_schemes_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];
}
/* Also copy the output names */
strcpy(output_options->basenames[section_id], basename);
strcpy(output_options->subdir_names[section_id], subdir_name);
memcpy(output_options->subsample[section_id], subsample,
swift_type_count * sizeof(int));
memcpy(output_options->subsample_fractions[section_id], subsample_fraction,
swift_type_count * sizeof(float));
} /* 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];
}
sprintf(output_options->basenames[default_id], "%s",
select_output_default_basename);
sprintf(output_options->subdir_names[default_id], "%s",
select_output_default_subdir_name);
for (int i = 0; i < swift_type_count; ++i)
output_options->subsample[default_id][i] =
select_output_default_subsample;
for (int i = 0; i < swift_type_count; ++i)
output_options->subsample_fractions[default_id][i] =
select_output_default_subsample_fraction;
}
}
/**
* @brief Write the output field parameters file
*
* @param filename The file to write.
* @param with_cosmology Use cosmological name variant?
* @param with_fof Use fof?
* @param with_stf Using Velociraptor STF?
*/
void io_write_output_field_parameter(const char* filename, int with_cosmology,
int with_fof, int with_stf) {
FILE* file = fopen(filename, "w");
if (file == NULL) error("Error opening file '%s'", filename);
/* Create a fake unit system for the snapshots */
struct unit_system snapshot_units;
units_init_cgs(&snapshot_units);
/* Loop over all particle types */
fprintf(file, "Default:\n");
for (int ptype = 0; ptype < swift_type_count; ptype++) {
struct io_props list[100];
int num_fields =
io_get_ptype_fields(ptype, list, with_cosmology, with_fof, with_stf);
if (num_fields == 0) continue;
/* Output a header for that particle type */
fprintf(file, " # Particle Type %s\n", part_type_names[ptype]);
/* Write all the fields of this particle type */
for (int i = 0; i < num_fields; ++i) {
char unit_buffer[FIELD_BUFFER_SIZE] = {0};
units_cgs_conversion_string(unit_buffer, &snapshot_units, list[i].units,
list[i].scale_factor_exponent);
/* Need to buffer with a maximal size - otherwise we can't read in again
* because comments are too long */
char comment_write_buffer[PARSER_MAX_LINE_SIZE / 2];
sprintf(comment_write_buffer, "%.*s", PARSER_MAX_LINE_SIZE / 2 - 1,
list[i].description);
/* If our string is too long, replace the last few characters (before
* \0) with ... for 'fancy printing' */
if (strlen(comment_write_buffer) > PARSER_MAX_LINE_SIZE / 2 - 3) {
strcpy(&comment_write_buffer[PARSER_MAX_LINE_SIZE / 2 - 4], "...");
}
fprintf(file, " %s_%s: %s # (%dD - %zd bytes / dim) %s : %s\n",
list[i].name, part_type_names[ptype], "on", list[i].dimension,
io_sizeof_type(list[i].type), comment_write_buffer, unit_buffer);
}
fprintf(file, "\n");
}
fclose(file);
printf(
"List of valid ouput fields for the particle in snapshots dumped in "
"'%s'.\n",
filename);
}