/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2017 Matthieu Schaller (schaller@strw.leidenuniv.nl). * Peter W. Draper (p.w.draper@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 #include /* This object's header. */ #include "xmf.h" /* Local headers. */ #include "common_io.h" #include "error.h" /** * @brief Return the basename of an HDF5 path. * * Need basename as XML paths are relative to the container, and XMF file is * written with the same baseName as the HDF5 snapshots. * * Note since the XMF file is written in the subdir (if it exists) we don't * want that as part of the name. But for distributed snapshots we need that * that directory. * * @param hdfFileName The full path to the HDF5 file * @param distributed Are we playing with a distributed snapshot? * @return the basename part of hdfFileName. */ static const char* xmf_basename(const char* hdfFileName, const int distributed) { int pos_last_slash = strlen(hdfFileName) - 1; /* Find last slash */ for (/* */; pos_last_slash >= 0; --pos_last_slash) if (hdfFileName[pos_last_slash] == '/') break; if (distributed) { /* Find one-before-last slash */ for (pos_last_slash--; pos_last_slash >= 0; --pos_last_slash) if (hdfFileName[pos_last_slash] == '/') break; } static char buffer[FILENAME_BUFFER_SIZE]; strcpy(buffer, &hdfFileName[pos_last_slash + 1]); return buffer; } /** * @brief Prepare the XMF file corresponding to a snapshot. * * @param fileName The name of the file. */ FILE* xmf_prepare_file(const char* fileName) { char buffer[1024]; char tempFileName[FILENAME_BUFFER_SIZE]; snprintf(tempFileName, FILENAME_BUFFER_SIZE, "%s.temp", fileName); FILE* xmfFile = fopen(fileName, "r"); FILE* tempFile = fopen(tempFileName, "w"); if (xmfFile == NULL) error("Unable to open current XMF file."); if (tempFile == NULL) error("Unable to open temporary file."); /* First we make a temporary copy of the XMF file and count the lines */ int counter = 0; while (fgets(buffer, 1024, xmfFile) != NULL) { counter++; fprintf(tempFile, "%s", buffer); } fclose(tempFile); fclose(xmfFile); /* We then copy the XMF file back up to the closing lines */ xmfFile = fopen(fileName, "w"); tempFile = fopen(tempFileName, "r"); if (xmfFile == NULL) error("Unable to open current XMF file."); if (tempFile == NULL) error("Unable to open temporary file."); int i = 0; while (fgets(buffer, 1024, tempFile) != NULL && i < counter - 3) { i++; fprintf(xmfFile, "%s", buffer); } fprintf(xmfFile, "\n"); fclose(tempFile); remove(tempFileName); return xmfFile; } /** * @brief Writes the begin of the XMF file * * @todo Exploit the XML nature of the XMF format to write a proper XML writer * and simplify all the XMF-related stuff. * * @param fileName The name of the file. */ void xmf_create_file(const char* fileName) { FILE* xmfFile = fopen(fileName, "w"); if (xmfFile == NULL) error("Unable to create XMF file."); fprintf(xmfFile, " \n"); fprintf(xmfFile, " \n"); fprintf( xmfFile, "\n"); fprintf(xmfFile, "\n"); fprintf(xmfFile, "\n\n"); fprintf(xmfFile, "\n"); fprintf(xmfFile, "\n"); fprintf(xmfFile, "\n"); fclose(xmfFile); } /** * @brief Writes the part of the XMF entry presenting the geometry of the * snapshot * * @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. */ void xmf_write_outputheader(FILE* xmfFile, const char* hdfFileName, float time) { /* Write end of file */ fprintf(xmfFile, "\n", hdfFileName); fprintf(xmfFile, "\n"); fprintf(xmfFile, " \n", output, time); fprintf(xmfFile, "\n \n"); fprintf(xmfFile, "\n"); fprintf(xmfFile, "\n"); fclose(xmfFile); } /** * @brief Writes the header of an XMF group for a given particle type. * * @param xmfFile The file to write to. * @param hdfFileName The name of the corresponding HDF5 file. * @param N The number of particles to write. * @param ptype The particle type we are writing. */ void xmf_write_groupheader(FILE* xmfFile, const char* hdfFileName, const int distributed, size_t N, enum part_type ptype) { fprintf(xmfFile, "\n\n", part_type_names[ptype]); fprintf(xmfFile, "\n", N); fprintf(xmfFile, "\n"); fprintf(xmfFile, "%s:/PartType%d/Coordinates\n", N, xmf_basename(hdfFileName, distributed), (int)ptype); fprintf(xmfFile, "\n \n", part_type_names[ptype]); } /** * @brief Writes the footer of an XMF group for a given particle type. * * @param xmfFile The file to write to. * @param ptype The particle type we are writing. */ void xmf_write_groupfooter(FILE* xmfFile, enum part_type ptype) { fprintf(xmfFile, " \n", part_type_names[ptype]); } /** * @brief Returns the precision of a given dataset type */ int xmf_precision(enum IO_DATA_TYPE type) { switch (type) { case INT: case FLOAT: return 4; break; case DOUBLE: return 8; break; case ULONGLONG: case LONGLONG: return 8; break; case UINT8: case CHAR: return 1; break; default: error("Unsupported type"); } return 0; } /** * @brief Returns the Xdmf type name of a given dataset type */ const char* xmf_type(enum IO_DATA_TYPE type) { switch (type) { case FLOAT: case DOUBLE: return "Float"; break; case INT: case ULONGLONG: case LONGLONG: return "Int"; break; case UINT8: case CHAR: return "Char"; break; default: error("Unsupported type"); } return ""; } /** * @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. * @param partTypeGroupName The name of the group containing the particles in * the HDF5 file. * @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. */ void xmf_write_line(FILE* xmfFile, const char* fileName, const int distributed, const char* partTypeGroupName, const char* name, size_t N, int dim, enum IO_DATA_TYPE type) { fprintf(xmfFile, "\n", name, dim == 1 ? "Scalar" : "Vector"); if (dim == 1) fprintf(xmfFile, "%s:%s/%s\n", N, xmf_type(type), xmf_precision(type), xmf_basename(fileName, distributed), partTypeGroupName, name); else fprintf(xmfFile, "%s:%s/%s\n", N, dim, xmf_type(type), xmf_precision(type), xmf_basename(fileName, distributed), partTypeGroupName, name); fprintf(xmfFile, "\n"); }