/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch)
* 2017 Pedro Gonnet (pedro.gonnet@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
#ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */
#ifdef WITH_CSDS
/* Some standard headers. */
#include
#include
#include
#include
#include
/* Define the particles first */
#include "part.h"
/* This object's header. */
#include "csds.h"
/* Local headers. */
#include "active.h"
#include "atomic.h"
#include "chemistry_csds.h"
#include "engine.h"
#include "error.h"
#include "gravity_csds.h"
#include "hydro_csds.h"
#include "star_formation_csds.h"
#include "stars_csds.h"
#include "units.h"
/**
* @brief log all particles in the engine.
*
* If this is the first log of all the particles,
* we include a flag and write the type of particle.
* This will be used by the reader to generate the index files.
*
* TODO use threadpool + csds function for multiple particles.
* @param log The #csds_writer.
* @param e The #engine.
* @param flag The flag to use when writing the particles
*/
void csds_log_all_particles(struct csds_writer *log, const struct engine *e,
const enum csds_special_flags flag) {
/* Ensure that enough space is available. */
csds_ensure_size(log, e);
/* csds_ensure_size is tracked separately. */
ticks tic = getticks();
/* some constants. */
const struct space *s = e->s;
/* log the parts. */
for (size_t i = 0; i < s->nr_parts; i++) {
struct part *p = &s->parts[i];
struct xpart *xp = &s->xparts[i];
if (!part_is_inhibited(p, e) && p->time_bin != time_bin_not_created) {
csds_log_part(log, p, xp, e, /* log_all_fields */ 1, flag,
/* flag_data */ 0);
}
}
/* log the gparts */
for (size_t i = 0; i < s->nr_gparts; i++) {
struct gpart *gp = &s->gparts[i];
if (!gpart_is_inhibited(gp, e) && gp->time_bin != time_bin_not_created &&
(gp->type == swift_type_dark_matter ||
gp->type == swift_type_dark_matter_background)) {
csds_log_gpart(log, gp, e, /* log_all_fields */ 1, flag,
/* flag_data */ 0);
}
}
/* log the parts */
for (size_t i = 0; i < s->nr_sparts; i++) {
struct spart *sp = &s->sparts[i];
if (!spart_is_inhibited(sp, e) && sp->time_bin != time_bin_not_created) {
csds_log_spart(log, sp, e, /* log_all_fields */ 1, flag,
/* flag_data */ 0);
}
}
if (s->nr_bparts > 0) error("Not implemented");
if (s->nr_sinks > 0) error("Not implemented");
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}
/**
* @brief Copy the particle fields into a given buffer.
*
* @param log The #csds_writer
* @param p The #part to copy.
* @param xp The #xpart to copy.
* @param e The #engine.
* @param mask The mask for the fields to write.
* @param offset The offset to the previous log.
* @param offset_new The offset of the current record.
* @param buff The buffer to use when writing.
* @param special_flags The data for the special flags.
*/
void csds_copy_part_fields(const struct csds_writer *log, const struct part *p,
const struct xpart *xp, const struct engine *e,
unsigned int mask, size_t *offset, size_t offset_new,
char *buff, const uint32_t special_flags) {
#ifdef SWIFT_DEBUG_CHECKS
if (mask == 0) {
error("You should always log at least one field.");
}
#endif
/* Write the header. */
buff = csds_write_record_header(buff, &mask, offset, offset_new);
/* Special flags */
if (mask & log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask) {
memcpy(buff, &special_flags,
log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size);
buff += log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
mask &= ~log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
}
/* Write the hydro fields */
for (int i = 0; i < log->number_fields[swift_type_gas]; i++) {
struct csds_field *field = &log->field_pointers[swift_type_gas][i];
/* Skip the fields that are not required. */
if (!(mask & field->mask)) continue;
/* Do we have a conversion function? */
if (field->conversion_hydro) {
char *tmp_buff = field->conversion_hydro(p, xp, e, buff);
/* Check that the correct number of bits are written */
if ((tmp_buff - buff) != (long int)field->size) {
error("The field %s wrote an unexpected number of bits", field->name);
}
}
/* Write it manually */
else {
if (field->use_xpart == 1)
memcpy(buff, ((char *)xp) + field->offset, field->size);
else if (field->use_xpart == 0)
memcpy(buff, ((char *)p) + field->offset, field->size);
else
error(
"It seems that you are using the wrong CSDS function in the hydro."
" You need to use csds_define_hydro_standard_field and not"
" the general one.");
}
/* Update the variables */
buff += field->size;
mask &= ~field->mask;
}
#ifdef SWIFT_DEBUG_CHECKS
if (mask) {
error("Requested logging of values not present in parts. %u", mask);
}
#endif
}
/**
* @brief Dump a #part to the log.
*
* @param log The #csds_writer
* @param p The #part to dump.
* @param xp The #xpart to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_part(struct csds_writer *log, const struct part *p,
struct xpart *xp, const struct engine *e,
const int log_all_fields, const enum csds_special_flags flag,
const int flag_data) {
csds_log_parts(log, p, xp, /* count= */ 1, e, log_all_fields, flag,
flag_data);
}
/**
* @brief Compute the size and the mask of all the fields that will be written.
*
* @param fields The list of fields to write.
* @param n_fields The number of fields to write.
* @param size (output) The size of all the fields.
* @param mask (output) The mask to use.
*/
void csds_compute_size_and_mask(struct csds_field *fields, int n_fields,
size_t *size, unsigned int *mask) {
*size = 0;
*mask = 0;
// TODO: write only some fields
for (int i = 0; i < n_fields; i++) {
*size += fields[i].size;
*mask |= fields[i].mask;
}
}
/**
* @brief Dump a group of #part to the log.
*
* @param log The #csds_writer.
* @param p The #part to dump.
* @param xp The #xpart to dump.
* @param count The number of particle to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_parts(struct csds_writer *log, const struct part *p,
struct xpart *xp, int count, const struct engine *e,
const int log_all_fields,
const enum csds_special_flags flag, const int flag_data) {
/* Build the special flag */
const int size_special_flag = log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
const uint32_t special_flags =
csds_pack_flags_and_data(flag, flag_data, swift_type_gas);
/* Compute the size of the buffer. */
size_t size = 0;
unsigned int mask = 0;
// TODO: write only some fields
csds_compute_size_and_mask(log->field_pointers[swift_type_gas],
log->number_fields[swift_type_gas], &size, &mask);
/* Add the flag */
if (flag != csds_flag_none) {
size += size_special_flag;
mask |= log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
}
size += CSDS_HEADER_SIZE;
size_t size_total = count * size;
/* Allocate a chunk of memory in the logfile of the right size. */
size_t offset_new;
char *buff =
(char *)csds_logfile_writer_get(&log->logfile, size_total, &offset_new);
#ifdef SWIFT_DEBUG_CHECKS
/* Save the buffer position in order to test if the requested buffer was
* really used */
const char *buff_before = buff;
#endif
/* Write the particles */
for (int i = 0; i < count; i++) {
/* reset the offset of the previous log */
if (flag == csds_flag_create || flag == csds_flag_mpi_enter) {
xp[i].csds_data.last_offset = 0;
}
/* Copy everything into the buffer */
csds_copy_part_fields(log, &p[i], &xp[i], e, mask,
&xp[i].csds_data.last_offset, offset_new, buff,
special_flags);
/* Update the pointers */
xp[i].csds_data.last_offset = offset_new;
xp[i].csds_data.steps_since_last_output = 0;
buff += size;
offset_new += size;
}
#ifdef SWIFT_DEBUG_CHECKS
/* Ensure that the buffer was fully used */
const int diff = buff - buff_before;
if (diff != (int)size_total) {
error("The requested buffer was not totally used: %i != %zi", diff,
size_total);
}
#endif
}
/**
* @brief Copy the particle fields into a given buffer.
*
* @param log The #csds_writer.
* @param sp The #spart to copy.
* @param e The #engine.
* @param mask The mask for the fields to write.
* @param offset The offset to the previous log.
* @param offset_new The offset of the current record.
* @param buff The buffer to use when writing.
* @param special_flags The data for the special flags.
*/
void csds_copy_spart_fields(const struct csds_writer *log,
const struct spart *sp, const struct engine *e,
unsigned int mask, size_t *offset,
size_t offset_new, char *buff,
const uint32_t special_flags) {
#ifdef SWIFT_DEBUG_CHECKS
if (mask == 0) {
error("You should always log at least one field.");
}
#endif
/* Write the header. */
buff = csds_write_record_header(buff, &mask, offset, offset_new);
/* Special flags */
if (mask & log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask) {
memcpy(buff, &special_flags,
log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size);
buff += log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
mask &= ~log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
}
/* Write the stellar fields */
for (int i = 0; i < log->number_fields[swift_type_stars]; i++) {
struct csds_field *field = &log->field_pointers[swift_type_stars][i];
/* Skip the fields that are not required. */
if (!(mask & field->mask)) continue;
/* Do we have a conversion function? */
if (field->conversion_stars) {
char *tmp_buff = field->conversion_stars(sp, e, buff);
/* Check that the correct number of bits are written */
if ((tmp_buff - buff) != (long int)field->size) {
error("The field %s wrote an unexpected number of bits", field->name);
}
}
/* Write it manually */
else {
memcpy(buff, ((char *)sp) + field->offset, field->size);
}
/* Update the variables */
buff += field->size;
mask &= ~field->mask;
}
#ifdef SWIFT_DEBUG_CHECKS
if (mask) {
error("Requested logging of values not present in sparts. %u", mask);
}
#endif
}
/**
* @brief Dump a #spart to the log.
*
* @param log The #csds_writer
* @param sp The #spart to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_spart(struct csds_writer *log, struct spart *sp,
const struct engine *e, const int log_all_fields,
const enum csds_special_flags flag, const int flag_data) {
csds_log_sparts(log, sp, /* count */ 1, e, log_all_fields, flag, flag_data);
}
/**
* @brief Dump a group of #spart to the log.
*
* @param log The #csds_writer
* @param sp The #spart to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param count The number of particle to dump.
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_sparts(struct csds_writer *log, struct spart *sp, int count,
const struct engine *e, const int log_all_fields,
const enum csds_special_flags flag, const int flag_data) {
/* Build the special flag */
const int size_special_flag = log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
const uint32_t special_flags =
csds_pack_flags_and_data(flag, flag_data, swift_type_stars);
/* Compute the size of the buffer. */
// TODO: write only some fields
unsigned int mask = 0;
size_t size = 0;
csds_compute_size_and_mask(log->field_pointers[swift_type_stars],
log->number_fields[swift_type_stars], &size,
&mask);
/* Add the flag */
if (flag != csds_flag_none) {
mask |= log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
size += size_special_flag;
}
size += CSDS_HEADER_SIZE;
size_t size_total = count * size;
/* Allocate a chunk of memory in the logfile of the right size. */
size_t offset_new;
char *buff =
(char *)csds_logfile_writer_get(&log->logfile, size_total, &offset_new);
#ifdef SWIFT_DEBUG_CHECKS
/* Save the buffer position in order to test if the requested buffer was
* really used */
const char *buff_before = buff;
#endif
for (int i = 0; i < count; i++) {
/* reset the offset of the previous log */
if (flag == csds_flag_create || flag == csds_flag_mpi_enter) {
sp[i].csds_data.last_offset = 0;
}
/* Copy everything into the buffer */
csds_copy_spart_fields(log, &sp[i], e, mask, &sp[i].csds_data.last_offset,
offset_new, buff, special_flags);
/* Update the pointers */
sp[i].csds_data.last_offset = offset_new;
sp[i].csds_data.steps_since_last_output = 0;
buff += size;
offset_new += size;
}
#ifdef SWIFT_DEBUG_CHECKS
/* Ensure that the buffer was fully used */
const int diff = buff - buff_before;
if (diff != (int)size_total) {
error("It seems that the requested buffer was not totally used: %i != %zi",
diff, size_total);
}
#endif
}
/**
* @brief Copy the particle fields into a given buffer.
*
* @param log The #csds_writer.
* @param gp The #gpart to copy.
* @param e The #engine.
* @param mask The mask for the fields to write.
* @param offset The offset to the previous log.
* @param offset_new The offset of the current record.
* @param buff The buffer to use when writing.
* @param special_flags The data of the special flag.
*/
void csds_copy_gpart_fields(const struct csds_writer *log,
const struct gpart *gp, const struct engine *e,
unsigned int mask, size_t *offset,
size_t offset_new, char *buff,
const uint32_t special_flags) {
#ifdef SWIFT_DEBUG_CHECKS
if (mask == 0) {
error("You should always log at least one field.");
}
#endif
/* Write the header. */
buff = csds_write_record_header(buff, &mask, offset, offset_new);
/* Special flags */
if (mask & log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask) {
memcpy(buff, &special_flags,
log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size);
buff += log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
mask &= ~log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
}
/* Write the gravity fields */
for (int i = 0; i < log->number_fields[swift_type_dark_matter]; i++) {
struct csds_field *field = &log->field_pointers[swift_type_dark_matter][i];
/* Skip the fields that are not required. */
if (!(mask & field->mask)) continue;
/* Do we have a conversion function? */
if (field->conversion_grav) {
char *tmp_buff = field->conversion_grav(gp, e, buff);
/* Check that the correct number of bits are written */
if ((tmp_buff - buff) != (long int)field->size) {
error("The field %s wrote an unexpected number of bits", field->name);
}
}
/* Write it manually */
else {
memcpy(buff, ((char *)gp) + field->offset, field->size);
}
/* Update the variables */
buff += field->size;
mask &= ~field->mask;
}
#ifdef SWIFT_DEBUG_CHECKS
if (mask) {
error("Requested logging of values not present in gparts. %u", mask);
}
#endif
}
/**
* @brief Dump a #gpart to the log.
*
* @param log The #csds_writer
* @param p The #gpart to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_gpart(struct csds_writer *log, struct gpart *p,
const struct engine *e, const int log_all_fields,
const enum csds_special_flags flag, const int flag_data) {
csds_log_gparts(log, p, /* count */ 1, e, log_all_fields, flag, flag_data);
}
/**
* @brief Dump a group of #gpart to the log.
*
* @param log The #csds_writer
* @param p The #gpart to dump.
* @param count The number of particle to dump.
* @param e The #engine.
* @param log_all_fields Should we log all the fields?
* @param flag The value of the special flags.
* @param flag_data The data to write for the flag.
*/
void csds_log_gparts(struct csds_writer *log, struct gpart *p, int count,
const struct engine *e, const int log_all_fields,
const enum csds_special_flags flag, const int flag_data) {
/* Build the special flag */
const int size_special_flag = log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].size;
const uint32_t special_flags =
csds_pack_flags_and_data(flag, flag_data, swift_type_dark_matter);
/* Compute the size of the buffer. */
/* As we might have some non DM particles, we cannot log_all_fields blindly */
int count_dm = 0;
// TODO: write only some fields
for (int i = 0; i < count; i++) {
/* Log only the dark matter */
if (p[i].type != swift_type_dark_matter &&
p[i].type != swift_type_dark_matter_background)
continue;
count_dm += 1;
}
unsigned int mask = 0;
size_t size = 0;
csds_compute_size_and_mask(log->field_pointers[swift_type_dark_matter],
log->number_fields[swift_type_dark_matter], &size,
&mask);
/* Add the flag */
if (flag != csds_flag_none) {
mask |= log->list_fields[CSDS_SPECIAL_FLAGS_INDEX].mask;
size += size_special_flag;
}
size += CSDS_HEADER_SIZE;
size_t size_total = size * count_dm;
/* Allocate a chunk of memory in the logfile of the right size. */
size_t offset_new;
char *buff =
(char *)csds_logfile_writer_get(&log->logfile, size_total, &offset_new);
#ifdef SWIFT_DEBUG_CHECKS
/* Save the buffer position in order to test if the requested buffer was
* really used */
const char *buff_before = buff;
#endif
for (int i = 0; i < count; i++) {
/* Log only the dark matter */
if (p[i].type != swift_type_dark_matter &&
p[i].type != swift_type_dark_matter_background)
continue;
/* reset the offset of the previous log */
if (flag == csds_flag_create || flag == csds_flag_mpi_enter) {
p[i].csds_data.last_offset = 0;
}
/* Copy everything into the buffer */
csds_copy_gpart_fields(log, &p[i], e, mask, &p[i].csds_data.last_offset,
offset_new, buff, special_flags);
/* Update the pointers */
p[i].csds_data.last_offset = offset_new;
p[i].csds_data.steps_since_last_output = 0;
buff += size;
offset_new += size;
}
#ifdef SWIFT_DEBUG_CHECKS
/* Ensure that the buffer was fully used */
const int diff = buff - buff_before;
if (diff != (int)size_total) {
error("It seems that the requested buffer was not totally used: %i != %zi",
diff, size_total);
}
#endif
}
/**
* @brief write a timestamp
*
* @param log The #csds_writer
* @param timestamp time to write
* @param time time or scale factor
* @param offset Pointer to the offset of the previous log of this particle;
* (return) offset of this log.
*/
void csds_log_timestamp(struct csds_writer *log, integertime_t timestamp,
double time, size_t *offset) {
struct csds_logfile_writer *logfile = &log->logfile;
/* Start by computing the size of the message. */
const int size =
log->list_fields[CSDS_TIMESTAMP_INDEX].size + CSDS_HEADER_SIZE;
/* Allocate a chunk of memory in the logfile of the right size. */
size_t offset_new;
char *buff = (char *)csds_logfile_writer_get(logfile, size, &offset_new);
/* Write the header. */
unsigned int mask = log->list_fields[CSDS_TIMESTAMP_INDEX].mask;
buff = csds_write_record_header(buff, &mask, offset, offset_new);
/* Store the timestamp. */
memcpy(buff, ×tamp, sizeof(integertime_t));
buff += sizeof(integertime_t);
/* Store the time. */
memcpy(buff, &time, sizeof(double));
/* Update the log message offset. */
*offset = offset_new;
}
/**
* @brief Ensure that the buffer is large enough for a step.
*
* Check if csds parameters are large enough to write all particles
* and ensure that enough space is available in the buffer.
*
* @param log The #csds_writer
* @param e The #engine.
*/
void csds_ensure_size(struct csds_writer *log, const struct engine *e) {
ticks tic = getticks();
/* count part memory */
size_t limit = 0;
/* count part memory */
limit += e->s->nr_parts;
/* count gpart memory */
limit += e->s->nr_gparts;
/* count spart memory. */
limit += e->s->nr_sparts;
// TODO improve estimate with the size of each particle
limit *= log->max_record_size;
/* ensure enough space in logfile */
csds_logfile_writer_ensure(&log->logfile, limit, log->buffer_scale * limit);
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}
/** @brief Generate the name of the logfile files
*
* @param log The #csds_writer.
* @param filename The filename of the logfile file.
*/
void csds_get_logfile_name(struct csds_writer *log, char *filename) {
sprintf(filename, "%s_%04i.dump", log->base_name, engine_rank);
}
/**
* @brief Initialize the variable list_fields.
*
* @param log The #csds_writer.
* @param e The #engine.
*/
void csds_init_masks(struct csds_writer *log, const struct engine *e) {
/* Set the pointers to 0 */
for (int i = 0; i < swift_type_count; i++) {
log->field_pointers[i] = NULL;
log->number_fields[i] = 0;
}
struct csds_field list[100];
int num_fields = 0;
/* The next fields must be the two first ones. */
/* Add the special flags (written manually => no need of offset) */
if (CSDS_SPECIAL_FLAGS_INDEX != 0) {
error("Expecting the special flags to be the first element.");
}
csds_define_common_field(list[CSDS_SPECIAL_FLAGS_INDEX], "SpecialFlags",
sizeof(uint32_t));
num_fields += 1;
/* Add the timestamp */
if (CSDS_TIMESTAMP_INDEX != 1) {
error("Expecting the timestamp to be the first element.");
}
csds_define_common_field(list[CSDS_TIMESTAMP_INDEX], "Timestamp",
sizeof(integertime_t) + sizeof(double));
list[num_fields].type = mask_for_timestep; // flag it as timestamp
num_fields += 1;
/* Initialize all the particles types */
for (int i = 0; i < swift_type_count; i++) {
int tmp_num_fields = 0;
struct csds_field *current = &list[num_fields];
enum mask_for_type mask_for_type;
/* Set the pointer */
log->field_pointers[i] = current;
switch (i) {
/* Hydro */
case swift_type_gas:
/* Set the mask type */
mask_for_type = mask_for_gas;
/* Set the masks */
tmp_num_fields = csds_hydro_define_fields(current);
tmp_num_fields +=
csds_chemistry_define_fields_parts(current + tmp_num_fields);
// TODO add cooling + SF
break;
/* Stars */
case swift_type_stars:
/* Set the mask type */
mask_for_type = mask_for_stars;
/* Set the masks */
tmp_num_fields = csds_stars_define_fields(current);
tmp_num_fields +=
csds_chemistry_define_fields_sparts(current + tmp_num_fields);
tmp_num_fields +=
csds_star_formation_define_fields(current + tmp_num_fields);
break;
case swift_type_dark_matter:
/* Set the mask type */
mask_for_type = mask_for_dark_matter;
/* Set the masks */
tmp_num_fields = csds_gravity_define_fields(current);
break;
default:
log->field_pointers[i] = NULL;
break;
}
/* Set the particle type */
for (int j = 0; j < tmp_num_fields; j++) {
current[j].type = mask_for_type;
}
/* Update the number of fields */
num_fields += tmp_num_fields;
log->number_fields[i] = tmp_num_fields;
}
/* Set the counter */
log->total_number_fields = num_fields;
/* Set the masks and ensure to have only one for the common fields
(e.g. Coordinates).
Initially we have (Name, mask, part_type):
- Coordinates, 0, 0
- Velocity, 0, 0
- Coordinates, 0, 1
And get:
- Coordinates, 1, 0
- Velocity, 2, 0
- Coordinates, 1, 1
*/
int mask = 0;
for (int i = 0; i < num_fields; i++) {
/* Skip the elements already processed. */
if (list[i].mask != 0) {
continue;
}
const char *name = list[i].name;
list[i].mask = 1 << mask;
/* Check if the field exists in the other particle type. */
for (int j = i + 1; j < num_fields; j++) {
/* Check if the name is the same */
if (strcmp(name, list[j].name) == 0) {
/* Check if the data are the same */
if (list[i].size != list[j].size) {
error("Found two same fields but with different data size (%s).",
name);
}
list[j].mask = 1 << mask;
}
}
mask += 1;
}
/* Check that we have enough available flags. */
if (mask >= 8 * CSDS_MASK_SIZE) {
error(
"Not enough available flags for all the fields. "
"Please reduce the number of output fields.");
}
/* Save the data */
size_t size_list = sizeof(struct csds_field) * num_fields;
log->list_fields = (struct csds_field *)malloc(size_list);
memcpy(log->list_fields, list, size_list);
/* Update the pointers */
for (int i = 0; i < swift_type_count; i++) {
if (log->field_pointers[i] != NULL) {
log->field_pointers[i] =
log->list_fields + (log->field_pointers[i] - list);
}
}
#ifdef SWIFT_DEBUG_CHECKS
if (e->nodeID == 0) {
message("The CSDS contains the following masks:");
for (int i = 0; i < log->total_number_fields; i++) {
message("%20s:\t mask=%03u\t size=%zi", log->list_fields[i].name,
log->list_fields[i].mask, log->list_fields[i].size);
}
}
#endif
}
/**
* @brief intialize the csds structure
*
* @param log The #csds_writer
* @param e The #engine.
* @param params The #swift_params
*/
void csds_init(struct csds_writer *log, const struct engine *e,
struct swift_params *params) {
ticks tic = getticks();
#ifdef WITH_MPI
/* Should be safe, but better to check */
if (e->nr_nodes >= 1 << 16)
error(
"The special flag does not contain enough bits"
"to store the information about the ranks.");
#endif
/* read parameters. */
log->delta_step = parser_get_param_int(params, "CSDS:delta_step");
size_t buffer_size =
parser_get_opt_param_float(params, "CSDS:initial_buffer_size", 0.5) * 1e9;
log->buffer_scale =
parser_get_opt_param_float(params, "CSDS:buffer_scale", 10);
parser_get_param_string(params, "CSDS:basename", log->base_name);
/* Initialize the list_fields */
csds_init_masks(log, e);
/* set initial value of parameters. */
log->timestamp_offset = 0;
/* generate logfile filename. */
char csds_name_file[PARSER_MAX_LINE_SIZE];
csds_get_logfile_name(log, csds_name_file);
/* Compute max size for a particle record. */
int max_size = CSDS_OFFSET_SIZE + CSDS_MASK_SIZE;
/* Loop over all fields except timestamp. */
for (int i = 0; i < log->total_number_fields; i++) {
/* Skip the timestamp */
if (i == CSDS_TIMESTAMP_INDEX) continue;
max_size += log->list_fields[i].size;
}
log->max_record_size = max_size;
/* init logfile. */
csds_logfile_writer_init(&log->logfile, csds_name_file, buffer_size);
if (e->verbose)
message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
clocks_getunit());
}
/**
* @brief Close logfile file and desallocate memory
*
* @param log The #csds_writer
*/
void csds_free(struct csds_writer *log) {
csds_logfile_writer_close(&log->logfile);
free(log->list_fields);
log->list_fields = NULL;
log->total_number_fields = 0;
}
/**
* @brief Write a file header to a csds file
*
* @param log The #csds_writer
*
*/
void csds_write_file_header(struct csds_writer *log) {
/* get required variables. */
struct csds_logfile_writer *logfile = &log->logfile;
/* Write the beginning of the header */
char *offset_first_record =
csds_logfile_writer_write_begining_header(logfile);
/* placeholder to write the number of unique masks. */
size_t file_offset = 0;
char *skip_unique_masks =
csds_logfile_writer_get(logfile, sizeof(unsigned int), &file_offset);
/* write masks. */
// loop over all mask type.
unsigned int unique_mask = 0;
for (int i = 0; i < log->total_number_fields; i++) {
/* Check if the mask was not already written */
int is_written = 0;
for (int j = 0; j < i; j++) {
if (log->list_fields[i].mask == log->list_fields[j].mask) {
is_written = 1;
break;
}
}
if (is_written) {
continue;
}
unique_mask += 1;
// mask name.
csds_write_data(logfile, &file_offset, CSDS_STRING_SIZE,
(const char *)&log->list_fields[i].name);
// mask size.
csds_write_data(logfile, &file_offset, sizeof(unsigned int),
(const char *)&log->list_fields[i].size);
}
memcpy(skip_unique_masks, &unique_mask, sizeof(unsigned int));
/* Write the number of fields per particle */
csds_write_data(logfile, &file_offset, sizeof(log->number_fields),
(const char *)log->number_fields);
/* Now write the order for each particle type */
for (int i = 0; i < swift_type_count; i++) {
int number_fields = log->number_fields[i];
if (number_fields == 0) continue;
struct csds_field *field = log->field_pointers[i];
/* Loop over all the fields from this particle type */
for (int k = 0; k < number_fields; k++) {
unsigned int current_mask = field[k].mask;
/* Find the index among all the fields. */
for (int m = 0; m < log->total_number_fields; m++) {
if (log->list_fields[m].mask == current_mask) {
csds_write_data(logfile, &file_offset, sizeof(int), (const char *)&m);
break;
}
}
}
}
/* Write the end of the header. */
csds_logfile_writer_write_end_header(logfile, offset_first_record);
}
/**
* @brief read record header
*
* @param buff The reading buffer
* @param mask The mask to read
* @param offset (return) the offset pointed by this record (absolute)
* @param cur_offset The current record offset
*
* @return Number of bytes read
*/
__attribute__((always_inline)) INLINE static int csds_read_record_header(
const char *buff, unsigned int *mask, size_t *offset, size_t cur_offset) {
memcpy(mask, buff, CSDS_MASK_SIZE);
buff += CSDS_MASK_SIZE;
*offset = 0;
memcpy(offset, buff, CSDS_OFFSET_SIZE);
*offset = cur_offset - *offset;
return CSDS_MASK_SIZE + CSDS_OFFSET_SIZE;
}
/**
* @brief Read a csds message and store the data in a #part.
*
* @param p The #part in which to store the values.
* @param offset Pointer to the offset of the csds message in the buffer,
* will be overwritten with the offset of the previous message.
* @param buff Pointer to the start of an encoded csds message.
*
* @return The mask containing the values read.
*/
int csds_read_part(const struct csds_writer *log, struct part *p,
size_t *offset, const char *buff) {
/* Jump to the offset. */
buff = &buff[*offset];
/* Start by reading the csds mask for this entry. */
const size_t cur_offset = *offset;
unsigned int mask = 0;
buff += csds_read_record_header(buff, &mask, offset, cur_offset);
for (int i = 0; i < log->total_number_fields; i++) {
if ((mask & log->list_fields[i].mask) &&
(log->list_fields[i].type == mask_for_gas)) {
const char *name = log->list_fields[i].name;
if (strcmp("Coordinates", name) == 0) {
memcpy(p->x, buff, 3 * sizeof(double));
buff += 3 * sizeof(double);
} else if (strcmp("Velocities", name) == 0) {
memcpy(p->v, buff, 3 * sizeof(float));
buff += 3 * sizeof(float);
} else if (strcmp("Accelerations", name) == 0) {
memcpy(p->a_hydro, buff, 3 * sizeof(float));
buff += 3 * sizeof(float);
} else if (strcmp("SmoothingLengths", name) == 0) {
memcpy(&p->h, buff, sizeof(float));
buff += sizeof(float);
} else if (strcmp("InternalEnergies", name) == 0) {
memcpy(&p->u, buff, sizeof(float));
buff += sizeof(float);
} else if (strcmp("Masses", name) == 0) {
memcpy(&p->mass, buff, sizeof(float));
buff += sizeof(float);
} else if (strcmp("Densities", name) == 0) {
memcpy(&p->rho, buff, sizeof(float));
buff += sizeof(float);
} else if (strcmp("ParticleIDs", name) == 0) {
memcpy(&p->id, buff, sizeof(long long));
buff += sizeof(long long);
} else if (strcmp("SPHENIXSecondaryFields", name) == 0) {
// No need to read it for testing
buff += 7 * sizeof(float);
} else {
error("Field '%s' not found", name);
}
}
}
/* Finally, return the mask of the values we just read. */
return mask;
}
/**
* @brief Read a csds message and store the data in a #gpart.
*
* @param p The #gpart in which to store the values.
* @param offset Pointer to the offset of the csds message in the buffer,
* will be overwritten with the offset of the previous message.
* @param buff Pointer to the start of an encoded csds message.
*
* @return The mask containing the values read.
*/
int csds_read_gpart(const struct csds_writer *log, struct gpart *p,
size_t *offset, const char *buff) {
/* Jump to the offset. */
buff = &buff[*offset];
/* Start by reading the csds mask for this entry. */
const size_t cur_offset = *offset;
unsigned int mask = 0;
buff += csds_read_record_header(buff, &mask, offset, cur_offset);
for (int i = 0; i < log->total_number_fields; i++) {
if ((mask & log->list_fields[i].mask) &&
(log->list_fields[i].type == mask_for_dark_matter)) {
const char *name = log->list_fields[i].name;
if (strcmp("Coordinates", name) == 0) {
memcpy(p->x, buff, 3 * sizeof(double));
buff += 3 * sizeof(double);
} else if (strcmp("Velocities", name) == 0) {
memcpy(p->v_full, buff, 3 * sizeof(float));
buff += 3 * sizeof(float);
} else if (strcmp("Accelerations", name) == 0) {
memcpy(p->a_grav, buff, 3 * sizeof(float));
buff += 3 * sizeof(float);
} else if (strcmp("ParticleIDs", name) == 0) {
memcpy(&p->id_or_neg_offset, buff, sizeof(long long));
buff += sizeof(long long);
} else if (strcmp("Masses", name) == 0) {
memcpy(&p->mass, buff, sizeof(float));
buff += sizeof(float);
} else {
error("Field '%s' not found", name);
}
}
}
/* Finally, return the mask of the values we just read. */
return mask;
}
/**
* @brief Read a csds message for a timestamp.
*
* @param log The #csds_writer.
* @param t The timestamp in which to store the value.
* @param time The time in which to store the value.
* @param offset Pointer to the offset of the csds message in the buffer,
* will be overwritten with the offset of the previous message.
* @param buff Pointer to the start of an encoded csds message.
*
* @return The mask containing the values read.
*/
int csds_read_timestamp(const struct csds_writer *log, integertime_t *t,
double *time, size_t *offset, const char *buff) {
/* Jump to the offset. */
buff = &buff[*offset];
/* Start by reading the csds mask for this entry. */
const size_t cur_offset = *offset;
unsigned int mask = 0;
buff += csds_read_record_header(buff, &mask, offset, cur_offset);
/* We are only interested in timestamps. */
if (!(mask & log->list_fields[CSDS_TIMESTAMP_INDEX].mask))
error("Trying to read timestamp from a particle.");
/* Make sure we don't have extra fields. */
if (mask != log->list_fields[CSDS_TIMESTAMP_INDEX].mask)
error("Timestamp message contains extra fields.");
/* Copy the timestamp value from the buffer. */
memcpy(t, buff, sizeof(integertime_t));
buff += sizeof(integertime_t);
/* Copy the timestamp value from the buffer. */
memcpy(time, buff, sizeof(double));
/* Finally, return the mask of the values we just read. */
return mask;
}
/**
* @brief Write a swift_params struct to the given FILE as a stream of bytes.
*
* @param log the struct
* @param stream the file stream
*/
void csds_struct_dump(const struct csds_writer *log, FILE *stream) {
restart_write_blocks((void *)log, sizeof(struct csds_writer), 1, stream,
"csds", "csds");
/* Write the masks */
restart_write_blocks((void *)log->list_fields, sizeof(struct csds_field),
log->total_number_fields, stream, "csds_masks",
"csds_masks");
}
/**
* @brief Restore a csds struct from the given FILE as a stream of
* bytes.
*
* @param csds the struct
* @param stream the file stream
*/
void csds_struct_restore(struct csds_writer *log, FILE *stream) {
/* Read the block */
restart_read_blocks((void *)log, sizeof(struct csds_writer), 1, stream, NULL,
"csds");
/* Read the masks */
const struct csds_field *old_list_fields = log->list_fields;
log->list_fields = (struct csds_field *)malloc(sizeof(struct csds_field) *
log->total_number_fields);
restart_read_blocks((void *)log->list_fields, sizeof(struct csds_field),
log->total_number_fields, stream, NULL, "csds_masks");
/* Restore the pointers */
for (int i = 0; i < swift_type_count; i++) {
if (log->field_pointers[i] == NULL) continue;
log->field_pointers[i] =
log->list_fields + (log->field_pointers[i] - old_list_fields);
}
/* Restart the logfile. */
char csds_name_file[PARSER_MAX_LINE_SIZE];
csds_get_logfile_name(log, csds_name_file);
csds_logfile_writer_restart(&log->logfile, csds_name_file);
}
#endif /* WITH_CSDS */
#endif /* HAVE_POSIX_FALLOCATE */