aboutsummaryrefslogtreecommitdiff
path: root/CarpetDev/CarpetIOF5/src/iof5.cc
diff options
context:
space:
mode:
Diffstat (limited to 'CarpetDev/CarpetIOF5/src/iof5.cc')
-rw-r--r--CarpetDev/CarpetIOF5/src/iof5.cc440
1 files changed, 315 insertions, 125 deletions
diff --git a/CarpetDev/CarpetIOF5/src/iof5.cc b/CarpetDev/CarpetIOF5/src/iof5.cc
index 388708c6e..01033b24b 100644
--- a/CarpetDev/CarpetIOF5/src/iof5.cc
+++ b/CarpetDev/CarpetIOF5/src/iof5.cc
@@ -3,6 +3,7 @@
#include <cctk_Parameters.h>
#include <util_Table.h>
+#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
@@ -12,12 +13,17 @@
#include <sstream>
#include <string>
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
#include <hdf5.h>
-#include <iof5.hh>
#include "CactusBase/IOUtil/src/ioGH.h"
#include "CactusBase/IOUtil/src/ioutil_CheckpointRecovery.h"
+#include "iof5.hh"
+
namespace CarpetIOF5 {
@@ -33,19 +39,19 @@ namespace CarpetIOF5 {
// Scheduled startup routine
- int CarpetIOF5_Startup ()
+ int CarpetIOF5_Startup()
{
- CCTK_RegisterBanner ("AMR F5 I/O provided by CarpetIOF5");
+ CCTK_RegisterBanner("AMR F5 I/O provided by CarpetIOF5");
- int const GHExtension = CCTK_RegisterGHExtension (CCTK_THORNSTRING);
- CCTK_RegisterGHExtensionSetupGH (GHExtension, SetupGH);
+ int const GHExtension = CCTK_RegisterGHExtension(CCTK_THORNSTRING);
+ CCTK_RegisterGHExtensionSetupGH(GHExtension, SetupGH);
return 0;
}
// Registered GH extension setup routine
- void* SetupGH (tFleshConfig* const fleshconfig,
- int const convLevel, cGH* const cctkGH)
+ void *SetupGH(tFleshConfig *const fleshconfig,
+ int const convLevel, cGH *const cctkGH)
{
DECLARE_CCTK_PARAMETERS;
@@ -53,18 +59,18 @@ namespace CarpetIOF5 {
int const ierr = IOUtil_RegisterRecover("CarpetIOF5 recovery", Input);
assert(not ierr);
- int const IOMethod = CCTK_RegisterIOMethod ("IOF5");
- CCTK_RegisterIOMethodOutputGH (IOMethod, OutputGH );
- CCTK_RegisterIOMethodTimeToOutput (IOMethod, TimeToOutput );
- CCTK_RegisterIOMethodTriggerOutput (IOMethod, TriggerOutput);
- CCTK_RegisterIOMethodOutputVarAs (IOMethod, OutputVarAs );
+ int const IOMethod = CCTK_RegisterIOMethod("IOF5");
+ CCTK_RegisterIOMethodOutputGH (IOMethod, OutputGH );
+ CCTK_RegisterIOMethodTimeToOutput (IOMethod, TimeToOutput );
+ CCTK_RegisterIOMethodTriggerOutput(IOMethod, TriggerOutput);
+ CCTK_RegisterIOMethodOutputVarAs (IOMethod, OutputVarAs );
// there no actual extension data structure
return NULL;
}
// Scheduled initialisation routine
- void CarpetIOF5_Init (CCTK_ARGUMENTS)
+ void CarpetIOF5_Init(CCTK_ARGUMENTS)
{
DECLARE_CCTK_ARGUMENTS;
@@ -76,55 +82,119 @@ namespace CarpetIOF5 {
- // Callbacks for CarpetIOHDF5's I/O method
-
- struct callback_arg_t {
- cGH const* cctkGH;
+ // A mechanism to keep the HDF5 output file open across multiple
+ // write operations:
+ hid_t file = H5I_INVALID_HID;
+ int keep_file_open = 0;
+ void enter_keep_file_open()
+ {
+ ++keep_file_open;
+ }
+ void leave_keep_file_open()
+ {
+ assert(keep_file_open > 0);
+ --keep_file_open;
+ if (keep_file_open==0 and file>=0) {
+ herr_t const herr = H5Fclose(file);
+ assert(not herr);
+ file = H5I_INVALID_HID;
+ }
+ }
+
+
+
+ // Interpret Cactus parameters to decide which variables have been
+ // selected for output
+ class selection_t {
+ int iteration;
+ int times_set;
+ vector<bool> selected;
+ vector<int> last_output_iteration;
+ static void do_output(int const vindex,
+ char const *const optstring,
+ void *const callback_arg)
+ {
+ static_cast<selection_t*>(callback_arg)->selected.at(vindex) = true;
+ }
+ public:
+ selection_t(): iteration(-1), times_set(-1)
+ {
+ }
+ bool is_selected(cGH const *const cctkGH, int const vindex)
+ {
+ DECLARE_CCTK_PARAMETERS;
+ // Check whether the parameter out_vars changed only once per
+ // iteration
+ if (cctkGH->cctk_iteration != iteration) {
+ iteration = cctkGH->cctk_iteration;
+ int const current_times_set =
+ CCTK_ParameterQueryTimesSet("out_vars", CCTK_THORNSTRING);
+ // Re-scan out_vars (which is somewhat expensive) only if it
+ // has changed
+ if (current_times_set > times_set) {
+ times_set = current_times_set;
+ selected.resize(CCTK_NumVars());
+ fill(selected.begin(), selected.end(), false);
+ // for (int n=0; n<CCTK_NumVars(); ++n) selected.at(n) = false;
+ CCTK_TraverseString(out_vars, do_output, this, CCTK_GROUP_OR_VAR);
+ }
+ }
+ return selected.at(vindex);
+ }
+ bool should_output(cGH const *const cctkGH, int const vindex)
+ {
+ last_output_iteration.resize(CCTK_NumVars(), -1);
+ return last_output_iteration.at(vindex) < cctkGH->cctk_iteration;
+ }
+ void did_output(cGH const *const cctkGH, int const vindex)
+ {
+ last_output_iteration.resize(CCTK_NumVars(), -1);
+ last_output_iteration.at(vindex) = cctkGH->cctk_iteration;
+ }
};
- void do_output (int const vindex,
- char const* const optstring,
- void* const callback_arg);
+ selection_t output_variables;
+
- int OutputGH (cGH const* const cctkGH)
+
+ // Callbacks for CarpetIOHDF5's I/O method
+
+ int OutputGH(cGH const *const cctkGH)
{
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
- static Carpet::Timer timer ("F5::OutputGH");
+ static Carpet::Timer timer("F5::OutputGH");
timer.start();
- callback_arg_t callback_arg;
- callback_arg.cctkGH = cctkGH;
- CCTK_TraverseString (out_vars, do_output, &callback_arg, CCTK_GROUP_OR_VAR);
+ enter_keep_file_open();
+ for (int vindex=0; vindex<CCTK_NumVars(); ++vindex) {
+ if (TimeToOutput(cctkGH, vindex)) {
+ TriggerOutput(cctkGH, vindex);
+ }
+ }
+ leave_keep_file_open();
timer.stop(0);
return 0;
}
- void do_output (int const vindex,
- char const* const optstring,
- void* const callback_arg_)
- {
- callback_arg_t& callback_arg =
- * static_cast<callback_arg_t*>(callback_arg_);
- cGH const* const cctkGH = callback_arg.cctkGH;
- if (TimeToOutput (cctkGH, vindex)) {
- TriggerOutput (cctkGH, vindex);
- }
- }
-
- int TimeToOutput (cGH const* const cctkGH, int const vindex)
+ int TimeToOutput(cGH const *const cctkGH, int const vindex)
{
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
int const numvars = CCTK_NumVars();
- assert (vindex>=0 and vindex<numvars);
+ assert(vindex>=0 and vindex<numvars);
// Output only in global mode
if (not do_global_mode) return 0;
+ // Output only selected variables
+ if (not output_variables.is_selected(cctkGH, vindex)) return 0;
+
+ if (not output_variables.should_output(cctkGH, vindex)) return 0;
+
// whether to output at this iteration
bool output_this_iteration = false;
@@ -144,69 +214,76 @@ namespace CarpetIOF5 {
return output_this_iteration ? 1 : 0;
}
- int TriggerOutput (cGH const* const cctkGH, int const vindex)
+ int TriggerOutput(cGH const *const cctkGH, int const vindex)
{
DECLARE_CCTK_PARAMETERS;
- char* const fullname = CCTK_FullName(vindex);
+ char *const fullname = CCTK_FullName(vindex);
int const gindex = CCTK_GroupIndexFromVarI(vindex);
- char* const groupname = CCTK_GroupName(gindex);
- for (char* p=groupname; *p; ++p) *p=tolower(*p);
- int const retval = OutputVarAs (cctkGH, fullname, groupname);
- free (groupname);
- free (fullname);
+ char *const groupname = CCTK_GroupName(gindex);
+ for (char *p=groupname; *p; ++p) *p=tolower(*p);
+ int const retval = OutputVarAs(cctkGH, fullname, groupname);
+ free(groupname);
+ free(fullname);
+
+ output_variables.did_output(cctkGH, vindex);
return retval;
}
- int OutputVarAs (cGH const* const cctkGH,
- const char* const varname, const char* const alias)
+ int OutputVarAs(cGH const *const cctkGH,
+ const char *const varname, const char *const alias)
{
DECLARE_CCTK_PARAMETERS;
- assert (is_level_mode());
+ assert(is_level_mode());
BEGIN_GLOBAL_MODE(cctkGH) {
DECLARE_CCTK_ARGUMENTS;
- CCTK_VInfo (CCTK_THORNSTRING,
- "F5::OutputVarAs: iteration=%d", cctk_iteration);
+ CCTK_VInfo(CCTK_THORNSTRING,
+ "F5::OutputVarAs: iteration=%d, variable=%s",
+ cctk_iteration, varname);
// We don't know how to open multiple files yet
- assert (CCTK_EQUALS (file_content, "everything"));
+ assert(CCTK_EQUALS(file_content, "everything"));
// Open file
static bool first_time = true;
// The file name doesn't matter since we currently write
// everything into a single file
- int const vindex = CCTK_VarIndex (varname);
- assert (vindex >= 0);
- string const basename = generate_basename (cctkGH, vindex);
+ int const vindex = CCTK_VarIndex(varname);
+ assert(vindex >= 0);
+ string const basename = generate_basename(cctkGH, vindex);
int const myproc = CCTK_MyProc(cctkGH);
int const proc = myproc;
string const name =
- create_filename (cctkGH, basename, proc, io_dir_output, first_time);
+ create_filename(cctkGH, basename, cctkGH->cctk_iteration, proc,
+ io_dir_output, first_time);
indent_t indent;
cout << indent << "process=" << proc << "\n";
+ enter_keep_file_open();
bool const truncate_file = first_time and IO_TruncateOutputFiles(cctkGH);
- hid_t const file =
- truncate_file ?
- H5Fcreate (name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) :
- H5Fopen (name.c_str(), H5F_ACC_RDWR , H5P_DEFAULT);
- assert (file >= 0);
+ if (file < 0) {
+ // Reuse file hid if file is already open
+ file =
+ truncate_file ?
+ H5Fcreate(name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) :
+ H5Fopen (name.c_str(), H5F_ACC_RDWR , H5P_DEFAULT);
+ assert(file >= 0);
+ }
first_time = false;
vector<bool> output_var(CCTK_NumVars());
output_var.at(vindex) = true;
- output (cctkGH, file, output_var, false);
+ output(cctkGH, file, output_var, false, true);
// Close file
- herr_t const herr = H5Fclose (file);
- assert (not herr);
+ leave_keep_file_open();
} END_GLOBAL_MODE;
@@ -217,83 +294,95 @@ namespace CarpetIOF5 {
// Checkpointing
- void Checkpoint (cGH const* const cctkGH, int const called_from)
+ void Checkpoint(cGH const *const cctkGH, int const called_from)
{
- assert (is_global_mode());
+ DECLARE_CCTK_PARAMETERS;
+
+ assert(is_global_mode());
+
+ CCTK_VInfo(CCTK_THORNSTRING,
+ "F5::Checkpoint: iteration=%d", cctkGH->cctk_iteration);
#if 0
// generate filenames for both the temporary and real checkpoint
// files
int const ioproc = CCTK_MyProc(cctkGH);
int const parallel_io = 1;
- char* const filename =
- IOUtil_AssembleFilename (cctkGH, NULL, "", ".f5",
- called_from, ioproc, not parallel_io);
- char* const tempname =
- IOUtil_AssembleFilename (cctkGH, NULL, ".tmp", ".f5",
- called_from, ioproc, not parallel_io);
+ char *const filename =
+ IOUtil_AssembleFilename(cctkGH, NULL, "", ".f5",
+ called_from, ioproc, not parallel_io);
+ char *const tempname =
+ IOUtil_AssembleFilename(cctkGH, NULL, ".tmp", ".f5",
+ called_from, ioproc, not parallel_io);
#endif
int const myproc = CCTK_MyProc(cctkGH);
int const proc = myproc;
string const name =
- create_filename (cctkGH, "checkpoint", proc, io_dir_checkpoint, true);
+ create_filename(cctkGH, "checkpoint", cctkGH->cctk_iteration, proc,
+ io_dir_checkpoint, true);
string const tempname =
- create_filename (cctkGH, "checkpoint.tmp", proc, io_dir_checkpoint, true);
+ create_filename(cctkGH, "checkpoint.tmp", cctkGH->cctk_iteration, proc,
+ io_dir_checkpoint, true);
hid_t const file =
- H5Fcreate (tempname.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
- assert (file >= 0);
+ H5Fcreate(tempname.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+ assert(file >= 0);
- vector<bool> output_var(CCTK_NumVars());
+ vector<bool> output_var(CCTK_NumVars(), false);
for (int gindex=0; gindex<CCTK_NumGroups(); ++gindex) {
- // only checkpoint groups with storage
- if (CCTK_QueryGroupStorageI(cctkGH, gindex) <= 0) continue;
- if (CCTK_NumVarsInGroupI(gindex) == 0) continue;
+ // We can't check for storage here, since this requires at least
+ // level mode
+ if (not CCTK_QueryGroupStorageI(cctkGH, gindex)) continue;
// do not checkpoint groups with a "checkpoint=no" tag
cGroup gdata;
- CCTK_GroupData (gindex, &gdata);
+ CCTK_GroupData(gindex, &gdata);
int const len =
- Util_TableGetString (gdata.tagstable, 0, NULL, "checkpoint");
+ Util_TableGetString(gdata.tagstable, 0, NULL, "checkpoint");
if (len > 0) {
char buf[1000];
- Util_TableGetString (gdata.tagstable, sizeof buf, buf, "checkpoint");
+ Util_TableGetString(gdata.tagstable, sizeof buf, buf, "checkpoint");
if (CCTK_EQUALS(buf, "no")) continue;
- assert (CCTK_EQUALS(buf, "yes"));
+ assert(CCTK_EQUALS(buf, "yes"));
}
- int const first_vindex = CCTK_FirstVarIndexI (gindex);
- int const num_vars = CCTK_NumVarsInGroupI (gindex);
- for (int vindex=first_vindex; vindex<first_vindex+num_vars; ++vindex) {
- output_var.at(vindex) = true;
+ int const first_vindex = CCTK_FirstVarIndexI(gindex);
+ int const num_vars = CCTK_NumVarsInGroupI(gindex);
+ if (num_vars > 0) {
+ for (int vindex=first_vindex; vindex<first_vindex+num_vars; ++vindex) {
+ output_var.at(vindex) = true;
+ }
}
}
- output (cctkGH, file, output_var, true);
+ // NOTE: We could write the metadata into only one of the process
+ // files (to save space), or write it into a separate metadata
+ // file
+ output(cctkGH, file, output_var, true, true);
// Close file
- herr_t const herr = H5Fclose (file);
- assert (not herr);
+ herr_t const herr = H5Fclose(file);
+ assert(not herr);
// Wait until all files have been written, then rename the
// checkpoint files
// TODO: ensure there were no errors
- CCTK_Barrier (cctkGH);
- int const ierr = rename (tempname.c_str(), name.c_str());
- assert (not ierr);
+ CCTK_Barrier(cctkGH);
+ int const ierr = rename(tempname.c_str(), name.c_str());
+ assert(not ierr);
}
- void CarpetIOF5_InitialDataCheckpoint (CCTK_ARGUMENTS)
+ void CarpetIOF5_InitialDataCheckpoint(CCTK_ARGUMENTS)
{
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
if (checkpoint and checkpoint_ID) {
- Checkpoint (cctkGH, CP_INITIAL_DATA);
+ Checkpoint(cctkGH, CP_INITIAL_DATA);
}
}
- void CarpetIOF5_EvolutionCheckpoint (CCTK_ARGUMENTS)
+ void CarpetIOF5_EvolutionCheckpoint(CCTK_ARGUMENTS)
{
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
@@ -305,11 +394,11 @@ namespace CarpetIOF5 {
checkpoint and (checkpoint_by_iteration or checkpoint_next);
if (do_checkpoint) {
- Checkpoint (cctkGH, CP_EVOLUTION_DATA);
+ Checkpoint(cctkGH, CP_EVOLUTION_DATA);
}
}
- void CarpetIOF5_TerminationCheckpoint (CCTK_ARGUMENTS)
+ void CarpetIOF5_TerminationCheckpoint(CCTK_ARGUMENTS)
{
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
@@ -320,15 +409,18 @@ namespace CarpetIOF5 {
bool const do_checkpoint = not did_checkpoint;
if (do_checkpoint) {
- Checkpoint (cctkGH, CP_EVOLUTION_DATA);
+ Checkpoint(cctkGH, CP_EVOLUTION_DATA);
}
}
}
- int Input (cGH* const cctkGH,
- char const* const basefilename, int const called_from)
+ // Recovery information
+ static int recovery_iteration = -1;
+
+ int Input(cGH *const cctkGH,
+ char const *const basefilename, int const called_from)
{
DECLARE_CCTK_PARAMETERS;
@@ -336,27 +428,31 @@ namespace CarpetIOF5 {
- assert (is_level_mode());
BEGIN_GLOBAL_MODE(cctkGH) {
DECLARE_CCTK_ARGUMENTS;
- CCTK_VInfo (CCTK_THORNSTRING, "F5::Input: iteration=%d", cctk_iteration);
-
-
-
- assert (called_from == CP_RECOVER_PARAMETERS or
- called_from == CP_RECOVER_DATA or
- called_from == FILEREADER_DATA);
+ assert(called_from == CP_RECOVER_PARAMETERS or
+ called_from == CP_RECOVER_DATA or
+ called_from == FILEREADER_DATA);
bool const in_recovery =
called_from == CP_RECOVER_PARAMETERS or
called_from == CP_RECOVER_DATA;
- // We don't know how to do this yet
- assert (called_from != CP_RECOVER_PARAMETERS);
+ if (in_recovery) {
+ CCTK_VInfo(CCTK_THORNSTRING, "F5::Input: recovering iteration %d",
+ recovery_iteration);
+ } else {
+ CCTK_VInfo(CCTK_THORNSTRING, "F5::Input: reading iteration %d",
+ cctk_iteration);
+ }
+ cout << "called_from=" <<
+ (called_from == CP_RECOVER_PARAMETERS ? "CP_RECOVER_PARAMETERS" :
+ called_from == CP_RECOVER_DATA ? "CP_RECOVER_DATA" :
+ called_from == FILEREADER_DATA ? "FILEREADER_DATA" :
+ NULL) << "\n";
// Determine which variables to read
- ioGH const* const ioUtilGH =
- (ioGH const*) CCTK_GHExtension (cctkGH, "IO");
+ ioGH const *const ioUtilGH = (ioGH const*)CCTK_GHExtension(cctkGH, "IO");
vector<bool> input_var(CCTK_NumVars(), true);
if (ioUtilGH->do_inVars) {
for (int n=0; n<CCTK_NumVars(); ++n) {
@@ -366,11 +462,13 @@ namespace CarpetIOF5 {
+ scatter_t scatter(cctkGH);
+
// Open file
// string const basename =
// in_recovery
// ? "checkpoint"
- // : generate_basename (cctkGH, CCTK_VarIndex("grid::r"));
+ // : generate_basename(cctkGH, CCTK_VarIndex("grid::r"));
string const basename = basefilename;
// Keep track of which files could be read, and which could not
@@ -382,8 +480,8 @@ namespace CarpetIOF5 {
// Loop over all (possible) files
for (int proc=myproc; ; proc+=nprocs) {
string const name =
- create_filename (cctkGH, basename, proc,
- in_recovery ? io_dir_recover : io_dir_input, false);
+ create_filename(cctkGH, basename, cctkGH->cctk_iteration, proc,
+ in_recovery ? io_dir_recover : io_dir_input, false);
bool file_exists;
H5E_BEGIN_TRY {
@@ -398,15 +496,19 @@ namespace CarpetIOF5 {
indent_t indent;
cout << indent << "process=" << proc << "\n";
- hid_t const file = H5Fopen (name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
- assert (file >= 0);
+ hid_t const file = H5Fopen(name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
+ assert(file >= 0);
// Iterate over all time slices
- input (cctkGH, file, input_var);
+ bool const input_past_timelevels = in_recovery;
+#warning "TODO: read metadata when recoverying parameters"
+ bool const input_metadata = false;
+ input(cctkGH, file, input_var, input_past_timelevels, input_metadata,
+ scatter);
// Close file
- herr = H5Fclose (file);
- assert (not herr);
+ herr = H5Fclose(file);
+ assert(not herr);
}
{
@@ -415,9 +517,10 @@ namespace CarpetIOF5 {
dist::comm());
if (maxfoundproc == -1) {
string const name =
- create_filename (cctkGH, basename, notfoundproc,
- in_recovery ? io_dir_recover : io_dir_input,
- false);
+ create_filename(cctkGH, basename,
+ cctkGH->cctk_iteration, notfoundproc,
+ in_recovery ? io_dir_recover : io_dir_input,
+ false);
CCTK_VWarn(CCTK_WARN_ALERT, __LINE__, __FILE__, CCTK_THORNSTRING,
"Could not read input file \"%s\"", name.c_str());
return 1;
@@ -434,4 +537,91 @@ namespace CarpetIOF5 {
return 0; // no error
}
+ int CarpetIOF5_RecoverParameters()
+ {
+#if 0
+ return IOUtil_RecoverParameters(Input, ".f5", "F5");
+#endif
+
+ DECLARE_CCTK_PARAMETERS;
+
+ char const *const IO_dir = IO_recover_dir;
+ char const *const F5_dir = recover_dir;
+ bool const use_IO_dir = strcmp(F5_dir, "") == 0;
+ char const *const my_recover_dir = use_IO_dir ? IO_dir : F5_dir;
+
+ DIR *const dir = opendir(my_recover_dir);
+ if (not dir) {
+ // The recovery directory does not exist
+ if (CCTK_Equals(recover, "autoprobe")) {
+ // This is harmless when "autoprobe" is used
+ CCTK_VInfo(CCTK_THORNSTRING,
+ "Recovery directory \"%s\" doesn't exist", my_recover_dir);
+ return 0;
+ } else {
+ // This is an error when "auto" is used
+ CCTK_VWarn(CCTK_WARN_ALERT, __LINE__, __FILE__, CCTK_THORNSTRING,
+ "Recovery directory \"%s\" doesn't exist", my_recover_dir);
+ return -2;
+ }
+ }
+
+ // Get the list of potential recovery files
+ char const *const my_recover_file = "checkpoint";
+ string const prefix = string(my_recover_file) + ".i";
+ string const infix = ".p";
+ string const suffix = ".f5";
+ assert(recovery_iteration < 0);
+ while (dirent *const file = readdir(dir)) {
+ char *p = file->d_name;
+
+ // First check the file prefix
+ if (prefix.compare(0, prefix.length(), p, prefix.length()) != 0) continue;
+ p += prefix.length();
+
+ // Now check if there is an iteration number following the file
+ // prefix
+ int const iter = strtol(p, &p, 10);
+ if (!*p) continue;
+
+ // Read the process number
+ if (infix.compare(0, infix.length(), p, infix.length()) != 0) continue;
+ p += infix.length();
+ int const proc = strtol(p, &p, 10);
+ if (!*p) continue;
+
+ // Finally check the file extension
+ if (suffix.compare(0, suffix.length(), p, suffix.length()) != 0) continue;
+ p += suffix.length();
+
+ // Check whether we read the whole string
+ if (*p) continue;
+
+ // Found a recovery file by that basename
+ recovery_iteration = max(recovery_iteration, iter);
+ }
+ closedir(dir);
+
+ // There is no recovery file
+ if (recovery_iteration < 0) {
+ if (CCTK_Equals(recover, "autoprobe")) {
+ // This is harmless when "autoprobe" is used
+ CCTK_VInfo(CCTK_THORNSTRING,
+ "No F5 checkpoint files with basefilename \"%s\" found in "
+ "recovery directory \"%s\"",
+ my_recover_file, my_recover_dir);
+ return 0;
+ } else {
+ // This is an error when "auto" is used
+ CCTK_VWarn(CCTK_WARN_ALERT, __LINE__, __FILE__, CCTK_THORNSTRING,
+ "No F5 checkpoint files with basefilename \"%s\" found in "
+ "recovery directory \"%s\"",
+ my_recover_file, my_recover_dir);
+ return -1;
+ }
+ }
+
+ return Input(NULL, "checkpoint", CP_RECOVER_PARAMETERS);
+ }
+
} // end namespace CarpetIOF5