-
Matthieu Schaller authoredMatthieu Schaller authored
xmf.c 8.62 KiB
/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
* 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 <http://www.gnu.org/licenses/>.
*
******************************************************************************/
/* Config parameters. */
#include "../config.h"
/* Some standard headers. */
#include <libgen.h>
#include <stdio.h>
#include <string.h>
/* 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.
*
* @param hdfFileName
* @return the basename part of hdfFileName.
*/
static const char* xmf_basename(const char* hdfFileName) {
static char buffer[FILENAME_BUFFER_SIZE];
strcpy(buffer, hdfFileName);
return basename(buffer);
}
/**
* @brief Prepare the XMF file corresponding to a snapshot.
*
* @param baseName The common part of the file name.
*/
FILE* xmf_prepare_file(const char* baseName) {
char buffer[1024];
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");
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.
*/
void xmf_create_file(const char* baseName) {
char fileName[FILENAME_BUFFER_SIZE];
snprintf(fileName, FILENAME_BUFFER_SIZE, "%s.xmf", baseName);
FILE* xmfFile = fopen(fileName, "w");
fprintf(xmfFile, "<?xml version=\"1.0\" ?> \n");
fprintf(xmfFile, "<!DOCTYPE Xdmf SYSTEM \"Xdmf.dtd\" []> \n");
fprintf(
xmfFile,
"<Xdmf xmlns:xi=\"http://www.w3.org/2003/XInclude\" Version=\"2.1\">\n");
fprintf(xmfFile, "<Domain>\n");
fprintf(xmfFile,
"<Grid Name=\"TimeSeries\" GridType=\"Collection\" "
"CollectionType=\"Temporal\">\n\n");
fprintf(xmfFile, "</Grid>\n");
fprintf(xmfFile, "</Domain>\n");
fprintf(xmfFile, "</Xdmf>\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, char* hdfFileName, float time) {
/* Write end of file */
fprintf(xmfFile, "<!-- XMF description for file: %s -->\n", hdfFileName);
fprintf(xmfFile,
"<Grid GridType=\"Collection\" CollectionType=\"Spatial\">\n");
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.
* @param output The number of this output.
* @param time The current simulation time.
*/
void xmf_write_outputfooter(FILE* xmfFile, int output, float time) {
/* Write end of the section of this time step */
fprintf(xmfFile,
"\n</Grid> <!-- End of meta-data for output=%04i, time=%f -->\n",
output, time);
fprintf(xmfFile, "\n</Grid> <!-- timeSeries -->\n");
fprintf(xmfFile, "</Domain>\n");
fprintf(xmfFile, "</Xdmf>\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, char* hdfFileName, size_t N,
enum part_type ptype) {
fprintf(xmfFile, "\n<Grid Name=\"%s\" GridType=\"Uniform\">\n",
part_type_names[ptype]);
fprintf(xmfFile,
"<Topology TopologyType=\"Polyvertex\" Dimensions=\"%zu\"/>\n", N);
fprintf(xmfFile, "<Geometry GeometryType=\"XYZ\">\n");
fprintf(xmfFile,
"<DataItem Dimensions=\"%zu 3\" NumberType=\"Double\" "
"Precision=\"8\" "
"Format=\"HDF\">%s:/PartType%d/Coordinates</DataItem>\n",
N, xmf_basename(hdfFileName), (int)ptype);
fprintf(xmfFile,
"</Geometry>\n <!-- Done geometry for %s, start of particle fields "
"list -->\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, "</Grid> <!-- End of meta-data for parttype=%s -->\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 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 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 char* partTypeGroupName, const char* name, size_t N,
int dim, enum IO_DATA_TYPE type) {
fprintf(xmfFile,
"<Attribute Name=\"%s\" AttributeType=\"%s\" Center=\"Node\">\n",
name, dim == 1 ? "Scalar" : "Vector");
if (dim == 1)
fprintf(xmfFile,
"<DataItem Dimensions=\"%zu\" NumberType=\"%s\" "
"Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
N, xmf_type(type), xmf_precision(type), xmf_basename(fileName),
partTypeGroupName, name);
else
fprintf(xmfFile,
"<DataItem Dimensions=\"%zu %d\" NumberType=\"%s\" "
"Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
N, dim, xmf_type(type), xmf_precision(type), xmf_basename(fileName),
partTypeGroupName, name);
fprintf(xmfFile, "</Attribute>\n");
}