common_io.c 17.8 KB
Newer Older
1
2
/*******************************************************************************
 * This file is part of SWIFT.
3
 * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
4
 *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk).
5
 *
6
7
8
9
 * 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.
10
 *
11
12
13
14
 * 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.
15
 *
16
17
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
20
21
22
23
24
25
26
 ******************************************************************************/

/* Config parameters. */
#include "../config.h"

#if defined(HAVE_HDF5)

/* Some standard headers. */
27
28
29
#include <hdf5.h>
#include <math.h>
#include <stddef.h>
30
31
32
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
33
34

/* MPI headers. */
35
36
37
#ifdef WITH_MPI
#include <mpi.h>
#endif
38

39
40
41
42
/* This object's header. */
#include "common_io.h"

/* Local includes. */
43
44
#include "const.h"
#include "error.h"
45
#include "kernel_hydro.h"
46
47
#include "part.h"
#include "units.h"
48
#include "version.h"
49

Matthieu Schaller's avatar
Matthieu Schaller committed
50
51
const char* particle_type_names[NUM_PARTICLE_TYPES] = {
    "Gas", "DM", "Boundary", "Dummy", "Star", "BH"};
52

53
/**
54
 * @brief Converts a C data type to the HDF5 equivalent.
55
56
 *
 * This function is a trivial wrapper around the HDF5 types but allows
57
58
 * to change the exact storage types matching the code types in a transparent
 *way.
59
 */
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
hid_t hdf5Type(enum DATA_TYPE type) {
  switch (type) {
    case INT:
      return H5T_NATIVE_INT;
    case UINT:
      return H5T_NATIVE_UINT;
    case LONG:
      return H5T_NATIVE_LONG;
    case ULONG:
      return H5T_NATIVE_ULONG;
    case LONGLONG:
      return H5T_NATIVE_LLONG;
    case ULONGLONG:
      return H5T_NATIVE_ULLONG;
    case FLOAT:
      return H5T_NATIVE_FLOAT;
    case DOUBLE:
      return H5T_NATIVE_DOUBLE;
    case CHAR:
      return H5T_C_S1;
    default:
      error("Unknown type");
      return 0;
  }
84
85
86
87
88
}

/**
 * @brief Returns the memory size of the data type
 */
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
size_t sizeOfType(enum DATA_TYPE type) {
  switch (type) {
    case INT:
      return sizeof(int);
    case UINT:
      return sizeof(unsigned int);
    case LONG:
      return sizeof(long);
    case ULONG:
      return sizeof(unsigned long);
    case LONGLONG:
      return sizeof(long long);
    case ULONGLONG:
      return sizeof(unsigned long long);
    case FLOAT:
      return sizeof(float);
    case DOUBLE:
      return sizeof(double);
    case CHAR:
      return sizeof(char);
    default:
      error("Unknown type");
      return 0;
  }
113
114
115
116
117
118
119
120
121
122
123
124
}

/**
 * @brief Reads an attribute from a given HDF5 group.
 *
 * @param grp The group from which to read.
 * @param name The name of the attribute to read.
 * @param type The #DATA_TYPE of the attribute.
 * @param data (output) The attribute read from the HDF5 group.
 *
 * Calls #error() if an error occurs.
 */
125
126
void readAttribute(hid_t grp, char* name, enum DATA_TYPE type, void* data) {
  hid_t h_attr = 0, h_err = 0;
127
128

  h_attr = H5Aopen(grp, name, H5P_DEFAULT);
129
130
131
  if (h_attr < 0) {
    error("Error while opening attribute '%s'", name);
  }
132
133

  h_err = H5Aread(h_attr, hdf5Type(type), data);
134
135
136
  if (h_err < 0) {
    error("Error while reading attribute '%s'", name);
  }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

  H5Aclose(h_attr);
}

/**
 * @brief Write an attribute to a given HDF5 group.
 *
 * @param grp The group in which to write.
 * @param name The name of the attribute to write.
 * @param type The #DATA_TYPE of the attribute.
 * @param data The attribute to write.
 * @param num The number of elements to write
 *
 * Calls #error() if an error occurs.
 */
152
153
void writeAttribute(hid_t grp, const char* name, enum DATA_TYPE type,
                    void* data, int num) {
154
155
  hid_t h_space = 0, h_attr = 0, h_err = 0;
  hsize_t dim[1] = {num};
156
157

  h_space = H5Screate(H5S_SIMPLE);
158
159
160
  if (h_space < 0) {
    error("Error while creating dataspace for attribute '%s'.", name);
  }
161
162

  h_err = H5Sset_extent_simple(h_space, 1, dim, NULL);
163
164
165
  if (h_err < 0) {
    error("Error while changing dataspace shape for attribute '%s'.", name);
  }
166
167

  h_attr = H5Acreate1(grp, name, hdf5Type(type), h_space, H5P_DEFAULT);
168
169
170
  if (h_attr < 0) {
    error("Error while creating attribute '%s'.", name);
  }
171
172

  h_err = H5Awrite(h_attr, hdf5Type(type), data);
173
174
175
  if (h_err < 0) {
    error("Error while reading attribute '%s'.", name);
  }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

  H5Sclose(h_space);
  H5Aclose(h_attr);
}

/**
 * @brief Write a string as an attribute to a given HDF5 group.
 *
 * @param grp The group in which to write.
 * @param name The name of the attribute to write.
 * @param str The string to write.
 * @param length The length of the string
 *
 * Calls #error() if an error occurs.
 */
191
192
void writeStringAttribute(hid_t grp, const char* name, const char* str,
                          int length) {
193
  hid_t h_space = 0, h_attr = 0, h_err = 0, h_type = 0;
194
195

  h_space = H5Screate(H5S_SCALAR);
196
197
198
  if (h_space < 0) {
    error("Error while creating dataspace for attribute '%s'.", name);
  }
199
200

  h_type = H5Tcopy(H5T_C_S1);
201
202
203
  if (h_type < 0) {
    error("Error while copying datatype 'H5T_C_S1'.");
  }
204
205

  h_err = H5Tset_size(h_type, length);
206
  if (h_err < 0) {
Matthieu Schaller's avatar
Matthieu Schaller committed
207
    error("Error while resizing attribute type to '%i'.", length);
208
  }
209
210

  h_attr = H5Acreate1(grp, name, h_type, h_space, H5P_DEFAULT);
211
212
213
  if (h_attr < 0) {
    error("Error while creating attribute '%s'.", name);
  }
214

215
216
217
218
  h_err = H5Awrite(h_attr, h_type, str);
  if (h_err < 0) {
    error("Error while reading attribute '%s'.", name);
  }
219
220
221
222
223
224
225
226

  H5Tclose(h_type);
  H5Sclose(h_space);
  H5Aclose(h_attr);
}

/**
 * @brief Writes a double value as an attribute
227
 * @param grp The group in which to write
228
229
230
 * @param name The name of the attribute
 * @param data The value to write
 */
231
void writeAttribute_d(hid_t grp, const char* name, double data) {
232
233
234
235
236
  writeAttribute(grp, name, DOUBLE, &data, 1);
}

/**
 * @brief Writes a float value as an attribute
237
 * @param grp The group in which to write
238
239
240
 * @param name The name of the attribute
 * @param data The value to write
 */
241
void writeAttribute_f(hid_t grp, const char* name, float data) {
242
243
244
245
246
  writeAttribute(grp, name, FLOAT, &data, 1);
}

/**
 * @brief Writes an int value as an attribute
247
 * @param grp The group in which to write
248
249
250
251
 * @param name The name of the attribute
 * @param data The value to write
 */

252
void writeAttribute_i(hid_t grp, const char* name, int data) {
253
254
255
256
257
  writeAttribute(grp, name, INT, &data, 1);
}

/**
 * @brief Writes a long value as an attribute
258
 * @param grp The group in which to write
259
260
261
 * @param name The name of the attribute
 * @param data The value to write
 */
262
void writeAttribute_l(hid_t grp, const char* name, long data) {
263
264
265
266
267
  writeAttribute(grp, name, LONG, &data, 1);
}

/**
 * @brief Writes a string value as an attribute
268
 * @param grp The group in which to write
269
270
271
 * @param name The name of the attribute
 * @param str The string to write
 */
272
void writeAttribute_s(hid_t grp, const char* name, const char* str) {
273
274
275
276
277
278
  writeStringAttribute(grp, name, str, strlen(str));
}

/**
 * @brief Writes the current Unit System
 * @param h_file The (opened) HDF5 file in which to write
279
 * @param us The UnitSystem used in the run
280
 */
281
282
void writeUnitSystem(hid_t h_file, struct UnitSystem* us) {
  hid_t h_grpunit = 0;
283
284

  h_grpunit = H5Gcreate1(h_file, "/Units", 0);
285
286
287
  if (h_grpunit < 0) error("Error while creating Unit System group");

  writeAttribute_d(h_grpunit, "Unit mass in cgs (U_M)",
288
                   units_get_base_unit(us, UNIT_MASS));
289
  writeAttribute_d(h_grpunit, "Unit length in cgs (U_L)",
290
                   units_get_base_unit(us, UNIT_LENGTH));
291
  writeAttribute_d(h_grpunit, "Unit time in cgs (U_t)",
292
                   units_get_base_unit(us, UNIT_TIME));
293
  writeAttribute_d(h_grpunit, "Unit current in cgs (U_I)",
294
                   units_get_base_unit(us, UNIT_CURRENT));
295
  writeAttribute_d(h_grpunit, "Unit temperature in cgs (U_T)",
296
                   units_get_base_unit(us, UNIT_TEMPERATURE));
297
298
299
300

  H5Gclose(h_grpunit);
}

301
302
303
304
305
306
307
308
309
310
311
/**
 * @brief Writes the code version to the file
 * @param h_file The (opened) HDF5 file in which to write
 */
void writeCodeDescription(hid_t h_file) {
  hid_t h_grpcode = 0;

  h_grpcode = H5Gcreate1(h_file, "/Code", 0);
  if (h_grpcode < 0) error("Error while creating code group");

  writeAttribute_s(h_grpcode, "Code Version", package_version());
312
313
  writeAttribute_s(h_grpcode, "Compiler Name", compiler_name());
  writeAttribute_s(h_grpcode, "Compiler Version", compiler_version());
314
315
  writeAttribute_s(h_grpcode, "Git Branch", git_branch());
  writeAttribute_s(h_grpcode, "Git Revision", git_revision());
316
317
318
319
  writeAttribute_s(h_grpcode, "HDF5 library version", hdf5_version());
#ifdef WITH_MPI
  writeAttribute_s(h_grpcode, "MPI library", mpi_version());
#ifdef HAVE_METIS
320
321
  writeAttribute_s(h_grpcode, "METIS library version", metis_version());
#endif
322
323
324
#else
  writeAttribute_s(h_grpcode, "MPI library", "Non-MPI version of SWIFT");
#endif
325
326
327
  H5Gclose(h_grpcode);
}

328
329
330
331
332
333
/* ------------------------------------------------------------------------------------------------
 * This part writes the XMF file descriptor enabling a visualisation through
 * ParaView
 * ------------------------------------------------------------------------------------------------
 */

334
335
336
337
338
339
340
/**
 * @brief Prepares the XMF file for the new entry
 *
 * Creates a temporary file on the disk in order to copy the right lines.
 *
 * @todo Use a proper XML library to avoid stupid copies.
 */
341
FILE* prepareXMFfile(const char* baseName) {
342
343
  char buffer[1024];

344
345
346
347
348
349
  char fileName[FILENAME_BUFFER_SIZE];
  char tempFileName[FILENAME_BUFFER_SIZE];
  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s.xmf", baseName);
  snprintf(tempFileName, FILENAME_BUFFER_SIZE, "%s_temp.xmf", baseName);
  FILE* xmfFile = fopen(fileName, "r");
  FILE* tempFile = fopen(tempFileName, "w");
350

351
  if (xmfFile == NULL) error("Unable to open current XMF file.");
352

353
  if (tempFile == NULL) error("Unable to open temporary file.");
354
355
356

  /* First we make a temporary copy of the XMF file and count the lines */
  int counter = 0;
357
358
359
360
  while (fgets(buffer, 1024, xmfFile) != NULL) {
    counter++;
    fprintf(tempFile, "%s", buffer);
  }
361
362
  fclose(tempFile);
  fclose(xmfFile);
363

364
  /* We then copy the XMF file back up to the closing lines */
365
366
  xmfFile = fopen(fileName, "w");
  tempFile = fopen(tempFileName, "r");
367

368
  if (xmfFile == NULL) error("Unable to open current XMF file.");
369

370
  if (tempFile == NULL) error("Unable to open temporary file.");
371

372
373
374
  int i = 0;
  while (fgets(buffer, 1024, tempFile) != NULL && i < counter - 3) {
    i++;
375
376
    fprintf(xmfFile, "%s", buffer);
  }
377
378
  fprintf(xmfFile, "\n");
  fclose(tempFile);
379
  remove(tempFileName);
380

381
382
383
384
385
386
  return xmfFile;
}

/**
 * @brief Writes the begin of the XMF file
 *
387
388
 * @todo Exploit the XML nature of the XMF format to write a proper XML writer
 *and simplify all the XMF-related stuff.
389
 */
390
391
392
393
394
void createXMFfile(const char* baseName) {

  char fileName[FILENAME_BUFFER_SIZE];
  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s.xmf", baseName);
  FILE* xmfFile = fopen(fileName, "w");
395
396
397

  fprintf(xmfFile, "<?xml version=\"1.0\" ?> \n");
  fprintf(xmfFile, "<!DOCTYPE Xdmf SYSTEM \"Xdmf.dtd\" []> \n");
398
399
400
  fprintf(
      xmfFile,
      "<Xdmf xmlns:xi=\"http://www.w3.org/2003/XInclude\" Version=\"2.1\">\n");
401
  fprintf(xmfFile, "<Domain>\n");
402
403
404
  fprintf(xmfFile,
          "<Grid Name=\"TimeSeries\" GridType=\"Collection\" "
          "CollectionType=\"Temporal\">\n\n");
405
406
407
408
409
410
411
412
413

  fprintf(xmfFile, "</Grid>\n");
  fprintf(xmfFile, "</Domain>\n");
  fprintf(xmfFile, "</Xdmf>\n");

  fclose(xmfFile);
}

/**
414
415
 * @brief Writes the part of the XMF entry presenting the geometry of the
 *snapshot
416
417
418
419
420
 *
 * @param xmfFile The file to write in.
 * @param hdfFileName The name of the HDF5 file corresponding to this output.
 * @param time The current simulation time.
 */
421
void writeXMFoutputheader(FILE* xmfFile, char* hdfFileName, float time) {
422
  /* Write end of file */
423

424
  fprintf(xmfFile, "<!-- XMF description for file: %s -->\n", hdfFileName);
425
426
  fprintf(xmfFile,
          "<Grid GridType=\"Collection\" CollectionType=\"Spatial\">\n");
427
428
429
430
431
432
433
  fprintf(xmfFile, "<Time Type=\"Single\" Value=\"%f\"/>\n", time);
}

/**
 * @brief Writes the end of the XMF file (closes all open markups)
 *
 * @param xmfFile The file to write in.
434
435
 * @param output The number of this output.
 * @param time The current simulation time.
436
 */
437
void writeXMFoutputfooter(FILE* xmfFile, int output, float time) {
438
  /* Write end of the section of this time step */
439

440
441
442
  fprintf(xmfFile,
          "\n</Grid> <!-- End of meta-data for output=%03i, time=%f -->\n",
          output, time);
443
  fprintf(xmfFile, "\n</Grid> <!-- timeSeries -->\n");
444
445
  fprintf(xmfFile, "</Domain>\n");
  fprintf(xmfFile, "</Xdmf>\n");
446

447
448
449
  fclose(xmfFile);
}

450
void writeXMFgroupheader(FILE* xmfFile, char* hdfFileName, size_t N,
Matthieu Schaller's avatar
Matthieu Schaller committed
451
                         enum PARTICLE_TYPE ptype) {
452
  fprintf(xmfFile, "\n<Grid Name=\"%s\" GridType=\"Uniform\">\n",
Matthieu Schaller's avatar
Matthieu Schaller committed
453
          particle_type_names[ptype]);
454
  fprintf(xmfFile,
Matthieu Schaller's avatar
Matthieu Schaller committed
455
          "<Topology TopologyType=\"Polyvertex\" Dimensions=\"%zi\"/>\n", N);
456
457
458
459
460
461
  fprintf(xmfFile, "<Geometry GeometryType=\"XYZ\">\n");
  fprintf(xmfFile,
          "<DataItem Dimensions=\"%zi 3\" NumberType=\"Double\" "
          "Precision=\"8\" "
          "Format=\"HDF\">%s:/PartType%d/Coordinates</DataItem>\n",
          N, hdfFileName, ptype);
462
463
464
465
  fprintf(xmfFile,
          "</Geometry>\n <!-- Done geometry for %s, start of particle fields "
          "list -->\n",
          particle_type_names[ptype]);
466
467
468
}

void writeXMFgroupfooter(FILE* xmfFile, enum PARTICLE_TYPE ptype) {
469
  fprintf(xmfFile, "</Grid> <!-- End of meta-data for parttype=%s -->\n",
Matthieu Schaller's avatar
Matthieu Schaller committed
470
          particle_type_names[ptype]);
471
472
}

473
474
475
476
477
/**
 * @brief Writes the lines corresponding to an array of the HDF5 output
 *
 * @param xmfFile The file in which to write
 * @param fileName The name of the HDF5 file associated to this XMF descriptor.
478
479
 * @param partTypeGroupName The name of the group containing the particles in
 *the HDF5 file.
480
481
482
483
484
485
486
 * @param name The name of the array in the HDF5 file.
 * @param N The number of particles.
 * @param dim The dimension of the quantity (1 for scalars, 3 for vectors).
 * @param type The type of the data to write.
 *
 * @todo Treat the types in a better way.
 */
487
void writeXMFline(FILE* xmfFile, char* fileName, char* partTypeGroupName,
Matthieu Schaller's avatar
Matthieu Schaller committed
488
                  char* name, size_t N, int dim, enum DATA_TYPE type) {
489
490
491
492
493
  fprintf(xmfFile,
          "<Attribute Name=\"%s\" AttributeType=\"%s\" Center=\"Node\">\n",
          name, dim == 1 ? "Scalar" : "Vector");
  if (dim == 1)
    fprintf(xmfFile,
494
495
496
            "<DataItem Dimensions=\"%zi\" NumberType=\"Double\" "
            "Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
            N, type == FLOAT ? 4 : 8, fileName, partTypeGroupName, name);
497
  else
498
    fprintf(xmfFile,
499
500
501
            "<DataItem Dimensions=\"%zi %d\" NumberType=\"Double\" "
            "Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
            N, dim, type == FLOAT ? 4 : 8, fileName, partTypeGroupName, name);
502
503
504
  fprintf(xmfFile, "</Attribute>\n");
}

505
506
507
508
509
510
511
512
513
514
/**
 * @brief Prepare the DM particles (in gparts) read in for the addition of the
 * other particle types
 *
 * This function assumes that the DM particles are all at the start of the
 * gparts array
 *
 * @param gparts The array of #gpart freshly read in.
 * @param Ndm The number of DM particles read in.
 */
Matthieu Schaller's avatar
Matthieu Schaller committed
515
void prepare_dm_gparts(struct gpart* const gparts, size_t Ndm) {
516
517

  /* Let's give all these gparts a negative id */
518
  for (size_t i = 0; i < Ndm; ++i) {
519

Matthieu Schaller's avatar
typo    
Matthieu Schaller committed
520
    /* 0 or negative ids are not allowed */
521
522
    if (gparts[i].id <= 0)
      error("0 or negative ID for DM particle %zd: ID=%lld", i, gparts[i].id);
Matthieu Schaller's avatar
Matthieu Schaller committed
523

524
    gparts[i].id = -gparts[i].id;
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
  }
}

/**
 * @brief Copy every #part into the corresponding #gpart and link them.
 *
 * This function assumes that the DM particles are all at the start of the
 * gparts array and adds the hydro particles afterwards
 *
 * @param parts The array of #part freshly read in.
 * @param gparts The array of #gpart freshly read in with all the DM particles
 *at the start
 * @param Ngas The number of gas particles read in.
 * @param Ndm The number of DM particles read in.
 */
Matthieu Schaller's avatar
Matthieu Schaller committed
540
541
542
void duplicate_hydro_gparts(struct part* const parts,
                            struct gpart* const gparts, size_t Ngas,
                            size_t Ndm) {
543

544
  for (size_t i = 0; i < Ngas; ++i) {
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570

    /* Duplicate the crucial information */
    gparts[i + Ndm].x[0] = parts[i].x[0];
    gparts[i + Ndm].x[1] = parts[i].x[1];
    gparts[i + Ndm].x[2] = parts[i].x[2];

    gparts[i + Ndm].v_full[0] = parts[i].v[0];
    gparts[i + Ndm].v_full[1] = parts[i].v[1];
    gparts[i + Ndm].v_full[2] = parts[i].v[2];

    gparts[i + Ndm].mass = parts[i].mass;

    /* Link the particles */
    gparts[i + Ndm].part = &parts[i];
    parts[i].gpart = &gparts[i + Ndm];
  }
}

/**
 * @brief Copy every DM #gpart into the dmparts array.
 *
 * @param gparts The array of #gpart containing all particles.
 * @param Ntot The number of #gpart.
 * @param dmparts The array of #gpart containg DM particles to be filled.
 * @param Ndm The number of DM particles.
 */
Matthieu Schaller's avatar
Matthieu Schaller committed
571
572
void collect_dm_gparts(const struct gpart* const gparts, size_t Ntot,
                       struct gpart* const dmparts, size_t Ndm) {
573

574
  size_t count = 0;
575
576

  /* Loop over all gparts */
577
  for (size_t i = 0; i < Ntot; ++i) {
578

Matthieu Schaller's avatar
Matthieu Schaller committed
579
580
    /* message("i=%zd count=%zd id=%lld part=%p", i, count, gparts[i].id,
     * gparts[i].part); */
Matthieu Schaller's avatar
Matthieu Schaller committed
581

582
    /* And collect the DM ones */
Matthieu Schaller's avatar
Matthieu Schaller committed
583
    if (gparts[i].id < 0LL) {
584
      memcpy(&dmparts[count], &gparts[i], sizeof(struct gpart));
585
      dmparts[count].id = -dmparts[count].id;
586
587
588
589
590
591
      count++;
    }
  }

  /* Check that everything is fine */
  if (count != Ndm)
592
    error("Collected the wrong number of dm particles (%zd vs. %zd expected)",
593
594
595
          count, Ndm);
}

596
#endif