/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2020 Josh Borrow (josh.borrow@durham.ac.uk) * * 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 . * ******************************************************************************/ #include /* Some standard headers. */ #include #include /* MPI headers. */ #ifdef WITH_MPI #include #endif /* This object's header. */ #include "output_options.h" /* Local headers. */ #include "common_io.h" #include "error.h" #include "parser.h" /** * @brief Initialise the output options struct with the information read * from file. Only rank 0 reads from file; this data is then broadcast * to all nodes. * * @param parameter_file the pre-parsed parameter file. * @param mpi_rank the MPI rank of this node. * @param output_options the empty output_options struct (return). **/ void output_options_init(struct swift_params* parameter_file, int mpi_rank, struct output_options* output_options) { /* Start by zero-ing everything */ bzero(output_options, sizeof(struct output_options)); /* Load select_output */ struct swift_params* select_output = (struct swift_params*)malloc(sizeof(struct swift_params)); if (select_output == NULL) { error("Error allocating memory for select output options."); } bzero(select_output, sizeof(struct swift_params)); if (mpi_rank == 0) { const int select_output_on = parser_get_opt_param_int( parameter_file, "Snapshots:select_output_on", 0); if (select_output_on) { char select_param_filename[PARSER_MAX_LINE_SIZE]; parser_get_param_string(parameter_file, "Snapshots:select_output", select_param_filename); message("Reading select output parameters from file '%s'", select_param_filename); parser_read_file(select_param_filename, select_output); } } #ifdef WITH_MPI MPI_Bcast(select_output, sizeof(struct swift_params), MPI_BYTE, 0, MPI_COMM_WORLD); #endif output_options->select_output = select_output; } /** * @brief Destroys an output_options instance. * * @param output_options the output_options struct to free the contents of. **/ void output_options_clean(struct output_options* output_options) { if (output_options) { free(output_options->select_output); } } /** * @brief Dumps the output_options struct to restart file * * @param output_options pointer to output options struct * @param stream bytestream to write to **/ void output_options_struct_dump(struct output_options* output_options, FILE* stream) { parser_struct_dump(output_options->select_output, stream); const size_t count_fields = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * swift_type_count; restart_write_blocks(output_options->num_fields_to_write, count_fields * sizeof(int), 1, stream, "output_options_fields", "output options"); const size_t count_chars = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * FILENAME_BUFFER_SIZE; restart_write_blocks(output_options->basenames, count_chars * sizeof(char), 1, stream, "output_options_basenames", "output options"); restart_write_blocks(output_options->subdir_names, count_chars * sizeof(char), 1, stream, "output_options_subdir_names", "output options"); const size_t count_sub = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * swift_type_count; restart_write_blocks(output_options->subsample, count_sub * sizeof(int), 1, stream, "output_options_subsample", "output options"); restart_write_blocks(output_options->subsample_fractions, count_sub * sizeof(float), 1, stream, "output_options_subsample_fractions", "output options"); } /** * @brief Loads the output_options struct from the restart file * * @param output_options pointer to the output options struct * @param stream bytestream to read from **/ void output_options_struct_restore(struct output_options* output_options, FILE* stream) { struct swift_params* select_output = (struct swift_params*)malloc(sizeof(struct swift_params)); bzero(select_output, sizeof(struct swift_params)); parser_struct_restore(select_output, stream); output_options->select_output = select_output; const size_t count_fields = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * swift_type_count; restart_read_blocks(output_options->num_fields_to_write, count_fields * sizeof(int), 1, stream, NULL, "output options_fields"); const size_t count_chars = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * FILENAME_BUFFER_SIZE; restart_read_blocks(output_options->basenames, count_chars * sizeof(char), 1, stream, NULL, "output_options_basenames"); restart_read_blocks(output_options->subdir_names, count_chars * sizeof(char), 1, stream, NULL, "output_options_subdir_names"); const size_t count_sub = (OUTPUT_LIST_MAX_NUM_OF_SELECT_OUTPUT_STYLES + 1) * swift_type_count; restart_read_blocks(output_options->subsample, count_sub * sizeof(int), 1, stream, NULL, "output_options_subsample"); restart_read_blocks(output_options->subsample_fractions, count_sub * sizeof(float), 1, stream, NULL, "output_options_subsample_fractions"); } /** * @brief Decides whether or not a given field should be written. Returns * a truthy value if yes, falsey if not. * * @param output_options pointer to the output options struct * @param snapshot_type pointer to a char array containing the type of * output (i.e. top level section in the yaml). * @param field_name pointer to a char array containing the name of the * relevant field. * @param ptype integer particle type * @param compression_level_current_default The default output strategy * based on the snapshot_type and part_type. * @param verbose The verbose level. * * @return should_write integer determining whether this field should be * written **/ enum lossy_compression_schemes output_options_get_field_compression( const struct output_options* output_options, const char* snapshot_type, const char* field_name, const enum part_type ptype, const enum lossy_compression_schemes compression_level_current_default, const int verbose) { /* Full name for the field path */ char field[PARSER_MAX_LINE_SIZE]; sprintf(field, "%.*s:%.*s_%s", FIELD_BUFFER_SIZE, snapshot_type, FIELD_BUFFER_SIZE, field_name, part_type_names[ptype]); char compression_level[FIELD_BUFFER_SIZE]; parser_get_opt_param_string( output_options->select_output, field, compression_level, lossy_compression_schemes_names[compression_level_current_default]); #ifdef SWIFT_DEBUG_CHECKS const int should_write = strcmp(lossy_compression_schemes_names[compression_do_not_write], compression_level); if (verbose) { message("Field: %s Write: %s Comp level: \"%s\"", field, should_write ? "True" : "False", compression_level); } #endif return compression_scheme_from_name(compression_level); } /** * @brief Return the default output strategy of a given particle type. * * This can only be "on" or "off". No lossy compression strategy can be * applied at the level of an entire particle type. * * @param output_params The parsed select output file. * @param snapshot_type The type of snapshot we are writing * @param ptype The #part_type we are considering. * @param verbose The verbose level. */ enum lossy_compression_schemes output_options_get_ptype_default_compression( struct swift_params* output_params, const char* snapshot_type, const enum part_type ptype, const int verbose) { /* Full name for the default path */ char field[PARSER_MAX_LINE_SIZE]; sprintf(field, "%.*s:Standard_%s", FIELD_BUFFER_SIZE, snapshot_type, part_type_names[ptype]); char compression_level[FIELD_BUFFER_SIZE]; parser_get_opt_param_string( output_params, field, compression_level, lossy_compression_schemes_names[compression_level_default]); /* Need to find out which of the entries this corresponds to... */ int level_index; for (level_index = 0; level_index < compression_level_count; level_index++) { if (!strcmp(lossy_compression_schemes_names[level_index], compression_level)) break; } /* Make sure that the supplied default option is either on or off, not a * compression strategy (these should only be set on a per-field basis) */ if (!(level_index == compression_do_not_write || level_index == compression_write_lossless)) { error( "A lossy default compression strategy was specified for snapshot " "type %s and particle type %d. This is not allowed, lossy " "compression must be set on a field-by-field basis.", snapshot_type, ptype); } #ifdef SWIFT_DEBUG_CHECKS /* Check whether we could translate the level string to a known entry. */ if (level_index >= compression_level_count) { error( "Could not resolve compression level \"%s\" as default compression " "level of particle type %s in snapshot type %s.", compression_level, part_type_names[ptype], snapshot_type); } if (verbose) { message( "Default compression %s, snapshot type %s, compression level %s and " "level code %d", part_type_names[ptype], snapshot_type, compression_level, level_index); } #endif return (enum lossy_compression_schemes)level_index; } /** * @brief Return the number of fields to be written for a ptype. * * @param output_options The output_options struct. * @param selection_name The current output selection name. * @param ptype The particle type index. */ int output_options_get_num_fields_to_write( const struct output_options* output_options, const char* selection_name, const int ptype) { /* Get the ID of the output selection in the structure */ int selection_id = parser_get_section_id(output_options->select_output, selection_name); #ifdef SWIFT_DEBUG_CHECKS /* The only situation where we might legitimately not find the selection * name is if it is the default. Everything else means trouble. */ if (strcmp(selection_name, select_output_header_default_name) && selection_id < 0) error( "Output selection '%s' could not be located in output_options " "structure. Please investigate.", selection_name); /* While we're at it, make sure the selection ID is not impossibly high */ if (selection_id >= output_options->select_output->sectionCount) { error( "Output selection '%s' was apparently located in index %d of the " "output_options structure, but this only has %d sections.", selection_name, selection_id, output_options->select_output->sectionCount); } #endif /* Special treatment for absent `Default` section */ if (selection_id < 0) { selection_id = output_options->select_output->sectionCount; } return output_options->num_fields_to_write[selection_id][ptype]; } /** * @brief Return the sub-directory and snapshot basename for the current output * selection. * * @param output_options The #output_options structure * @param selection_name The current output selection name. * @param default_subdirname The default general sub-directory name. * @param default_basename The default general snapshot base name. * @param subdir_name (return) The sub-directory name to use for this dump. * @param basename (return) The snapshot base name to use for this dump. */ void output_options_get_basename(const struct output_options* output_options, const char* selection_name, const char* default_subdirname, const char* default_basename, char subdir_name[FILENAME_BUFFER_SIZE], char basename[FILENAME_BUFFER_SIZE]) { /* Get the ID of the output selection in the structure */ int selection_id = parser_get_section_id(output_options->select_output, selection_name); /* Special treatment for absent `Default` section */ if (selection_id < 0) { selection_id = output_options->select_output->sectionCount; } /* If the default keyword is found, we use the name provided * in the param file (not the select output!), aka. the argument * of the function. */ if (strcmp(output_options->basenames[selection_id], select_output_default_basename) == 0) { sprintf(basename, "%s", default_basename); } else { sprintf(basename, "%s", output_options->basenames[selection_id]); } /* If the default keyword is found, we use the subdir name provided * in the param file (not the select output!), aka. the argument * of the function. */ if (strcmp(output_options->subdir_names[selection_id], select_output_default_subdir_name) == 0) { sprintf(subdir_name, "%s", default_subdirname); } else { sprintf(subdir_name, "%s", output_options->subdir_names[selection_id]); } } /** * @brief Return the subsampling fraction for the current output selection. * * @param output_options The #output_options structure. * @param selection_name The current output selection name. * @param default_subsample The default general subsampling status. * @param default_subsample_fraction The default general subsampling fraction. * @param subsample (return) The subsampling status to use for this dump. * @param subsample_fraction (return) The subsampling fraction to use for this * dump. */ void output_options_get_subsampling( const struct output_options* output_options, const char* selection_name, const int default_subsample[swift_type_count], const float default_subsample_fraction[swift_type_count], int subsample[swift_type_count], float subsample_fraction[swift_type_count]) { /* Get the ID of the output selection in the structure */ int selection_id = parser_get_section_id(output_options->select_output, selection_name); /* Special treatment for absent `Default` section */ if (selection_id < 0) { selection_id = output_options->select_output->sectionCount; } /* If the default keyword is found, we use the subsample flag provided * in the param file (not the select output!), aka. the argument * of the function. */ if (output_options->subsample[selection_id][0] == select_output_default_subsample) { memcpy(subsample, default_subsample, sizeof(int) * swift_type_count); } else { memcpy(subsample, output_options->subsample[selection_id], sizeof(int) * swift_type_count); } /* If the default keyword is found, we use the subsample fraction provided * in the param file (not the select output!), aka. the argument * of the function. */ if (output_options->subsample_fractions[selection_id][0] == select_output_default_subsample_fraction) { memcpy(subsample_fraction, default_subsample_fraction, sizeof(float) * swift_type_count); } else { memcpy(subsample_fraction, output_options->subsample_fractions[selection_id], sizeof(float) * swift_type_count); } }