/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2021 John Helly (j.c.helly@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 .
*
******************************************************************************/
/* Config parameters. */
#include
/* Some standard headers. */
#include
#include
/* This object's header. */
#include "lightcone/lightcone_array.h"
/* Local headers */
#include "common_io.h"
#include "cosmology.h"
#include "engine.h"
#include "error.h"
#include "lightcone/lightcone.h"
#include "lightcone/lightcone_particle_io.h"
#include "lightcone/lightcone_replications.h"
#include "parser.h"
#include "particle_buffer.h"
#include "periodic.h"
#include "restart.h"
#include "space.h"
#include "timeline.h"
#include "tools.h"
/**
* @brief Initialise the properties of the lightcone code.
*
*/
void lightcone_array_init(struct lightcone_array_props *props,
const struct space *s, const struct cosmology *cosmo,
struct swift_params *params,
const struct unit_system *internal_units,
const struct phys_const *physical_constants,
const int verbose) {
/* Determine number of lightcones */
props->nr_lightcones = 0;
for (int lightcone_nr = 0; lightcone_nr <= MAX_LIGHTCONES;
lightcone_nr += 1) {
char name[PARSER_MAX_LINE_SIZE];
check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d:enabled",
props->nr_lightcones);
if (parser_get_opt_param_int(params, name, 0)) {
props->nr_lightcones += 1;
}
}
if (engine_rank == 0)
message("found %d lightcones to generate", props->nr_lightcones);
/* Allocate array of lightcones */
props->lightcone = (struct lightcone_props *)malloc(
sizeof(struct lightcone_props) * props->nr_lightcones);
if (!props->lightcone) error("Failed to allocate lightcone array");
/* Initialise lightcones */
props->nr_lightcones = 0;
for (int lightcone_nr = 0; lightcone_nr <= MAX_LIGHTCONES;
lightcone_nr += 1) {
char name[PARSER_MAX_LINE_SIZE];
check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d:enabled",
props->nr_lightcones);
if (parser_get_opt_param_int(params, name, 0)) {
check_snprintf(name, PARSER_MAX_LINE_SIZE, "Lightcone%d",
props->nr_lightcones);
lightcone_init(props->lightcone + lightcone_nr, lightcone_nr, s, cosmo,
params, internal_units, physical_constants, verbose);
props->nr_lightcones += 1;
}
}
/* Check lightcones have unique output file names */
for (int i = 0; i < props->nr_lightcones; i += 1) {
for (int j = 0; j < props->nr_lightcones; j += 1) {
if (i != j) {
const struct lightcone_props *lc1 = props->lightcone + i;
const struct lightcone_props *lc2 = props->lightcone + j;
if (strcmp(lc1->basename, lc2->basename) == 0)
error("Lightcones must have unique basenames!");
}
}
}
props->verbose = verbose;
}
void lightcone_array_clean(struct lightcone_array_props *props) {
for (int i = 0; i < props->nr_lightcones; i += 1)
lightcone_clean(props->lightcone + i);
free(props->lightcone);
}
void lightcone_array_struct_dump(const struct lightcone_array_props *props,
FILE *stream) {
struct lightcone_array_props tmp = *props;
tmp.lightcone = NULL;
restart_write_blocks((void *)&tmp, sizeof(struct lightcone_array_props), 1,
stream, "lightcone_array_props",
"lightcone_array_props");
for (int i = 0; i < props->nr_lightcones; i += 1)
lightcone_struct_dump(props->lightcone + i, stream);
}
void lightcone_array_struct_restore(struct lightcone_array_props *props,
FILE *stream) {
restart_read_blocks((void *)props, sizeof(struct lightcone_array_props), 1,
stream, NULL, "lightcone_array_props");
props->lightcone = (struct lightcone_props *)malloc(
sizeof(struct lightcone_props) * props->nr_lightcones);
if (!props->lightcone) error("Failed to allocate lightcone array");
for (int i = 0; i < props->nr_lightcones; i += 1)
lightcone_struct_restore(props->lightcone + i, stream);
}
void lightcone_array_prepare_for_step(struct lightcone_array_props *props,
const struct cosmology *cosmo,
const integertime_t ti_earliest_undrifted,
const integertime_t ti_current) {
for (int i = 0; i < props->nr_lightcones; i += 1)
lightcone_prepare_for_step(props->lightcone + i, cosmo,
ti_earliest_undrifted, ti_current);
for (int ptype = 0; ptype < swift_type_count; ptype += 1) {
props->check_type_for_crossing[ptype] = 0;
for (int i = 0; i < props->nr_lightcones; i += 1) {
if (props->lightcone[i].check_type_for_crossing[ptype]) {
props->check_type_for_crossing[ptype] = 1;
}
}
if (props->check_type_for_crossing[ptype] && props->verbose &&
engine_rank == 0) {
message("need to check type %s for crossing at this step",
part_type_names[ptype]);
}
}
}
int lightcone_array_trigger_map_update(struct lightcone_array_props *props) {
for (int i = 0; i < props->nr_lightcones; i += 1) {
if (lightcone_trigger_map_update(props->lightcone + i)) return 1;
}
return 0;
}
/**
* @brief Flush buffers for all lightcones in the array
*
* Buffers are flushed if they get large or a flush is forced
* by setting one of the input flags.
*
* props the #lightcone_array_props struct
* flush_map_updates force full update of the healpix maps
* flush_particles force output of all buffered particles
* end_file start a new file next time particles are written out
* dump_all_shells immediately output all remaining healpix maps
*
*/
void lightcone_array_flush(struct lightcone_array_props *props,
struct threadpool *tp, const struct cosmology *cosmo,
const struct unit_system *internal_units,
const struct unit_system *snapshot_units,
int flush_map_updates, int flush_particles,
int end_file, int dump_all_shells) {
if (props->verbose) lightcone_array_report_memory_use(props);
/* Loop over lightcones */
const int nr_lightcones = props->nr_lightcones;
for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) {
/* Get a pointer to this lightcone */
struct lightcone_props *lc_props = props->lightcone + lightcone_nr;
/* Apply lightcone map updates if requested */
if (flush_map_updates) lightcone_flush_map_updates(lc_props, tp);
/* Flush particle buffers if they're large or flag is set */
lightcone_flush_particle_buffers(lc_props, cosmo->a, internal_units,
snapshot_units, flush_particles, end_file);
/* Write out any completed healpix maps */
lightcone_dump_completed_shells(lc_props, tp, cosmo, internal_units,
snapshot_units, dump_all_shells,
/*need_flush=*/!flush_map_updates);
}
}
/**
* @brief Make a refined replication list for each lightcone
*
* Returns an array of struct #replication_list. Must be freed
* with lightcone_array_free_replications().
*
* props the #lightcone_array_props struct
* cell the #cell for which we're making replication lists
*
*/
struct replication_list *lightcone_array_refine_replications(
struct lightcone_array_props *props, const struct cell *cell) {
/* Get number of lightcones */
const int nr_lightcones = props->nr_lightcones;
/* Allocate a replication list for each lightcone */
struct replication_list *lists = (struct replication_list *)malloc(
sizeof(struct replication_list) * nr_lightcones);
/* Loop over lightcones */
for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) {
/* Make refined replication list for this lightcone */
struct lightcone_props *lightcone = props->lightcone + lightcone_nr;
replication_list_subset_for_cell(&lightcone->replication_list, cell,
lightcone->observer_position,
lists + lightcone_nr);
}
return lists;
}
/**
* @brief Free lists returned by lightcone_array_refine_replications
*
* props the #lightcone_array_props struct
* lists the array of struct #replication_list to free
*
*/
void lightcone_array_free_replications(struct lightcone_array_props *props,
struct replication_list *lists) {
/* Get number of lightcones */
const int nr_lightcones = props->nr_lightcones;
/* Loop over lightcones and clean replication lists */
for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) {
replication_list_clean(lists + lightcone_nr);
}
/* Free replication list array */
free(lists);
}
/**
* @brief Write the index file for each lightcone
*
* props the #lightcone_array_props struct
*
*/
void lightcone_array_write_index(struct lightcone_array_props *props,
const struct unit_system *internal_units,
const struct unit_system *snapshot_units) {
/* Get number of lightcones */
const int nr_lightcones = props->nr_lightcones;
/* Loop over lightcones and clean replication lists */
for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) {
lightcone_write_index(props->lightcone + lightcone_nr, internal_units,
snapshot_units);
}
}
void lightcone_array_report_memory_use(struct lightcone_array_props *props) {
long long memuse_local[4] = {0LL, 0LL, 0LL, 0LL};
/* Get number of lightcones */
const int nr_lightcones = props->nr_lightcones;
/* Loop over lightcones and clean replication lists */
for (int lightcone_nr = 0; lightcone_nr < nr_lightcones; lightcone_nr += 1) {
/* Accumulate mmeory use of this lightcone */
size_t particle_buffer_bytes;
size_t map_buffer_bytes;
size_t pixel_data_bytes;
lightcone_memory_use(&props->lightcone[lightcone_nr],
&particle_buffer_bytes, &map_buffer_bytes,
&pixel_data_bytes);
memuse_local[0] += particle_buffer_bytes;
memuse_local[1] += map_buffer_bytes;
memuse_local[2] += pixel_data_bytes;
}
memuse_local[3] = memuse_local[0] + memuse_local[1] + memuse_local[2];
/* Find min and max memory over MPI ranks */
long long memuse_min[4];
long long memuse_max[4];
#ifdef WITH_MPI
MPI_Reduce(memuse_local, memuse_min, 4, MPI_LONG_LONG, MPI_MIN, 0,
MPI_COMM_WORLD);
MPI_Reduce(memuse_local, memuse_max, 4, MPI_LONG_LONG, MPI_MAX, 0,
MPI_COMM_WORLD);
#else
for (int i = 0; i < 4; i += 1) {
memuse_min[i] = memuse_local[i];
memuse_max[i] = memuse_local[i];
}
#endif
/* Report memory use, if non-zero */
if (engine_rank == 0 && memuse_max[3] > 0) {
const long long MB = 1024 * 1024;
message("particle buffer Mbytes: min=%lldMB, max=%lldMB",
memuse_min[0] / MB, memuse_max[0] / MB);
message("map update buffer Mbytes: min=%lldMB, max=%lldMB",
memuse_min[1] / MB, memuse_max[1] / MB);
message("map pixel data Mbytes: min=%lldMB, max=%lldMB", memuse_min[2] / MB,
memuse_max[2] / MB);
message("total lightcone data Mbytes: min=%lldMB, max=%lldMB",
memuse_min[3] / MB, memuse_max[3] / MB);
}
}