Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
CardiacSimulationArchiver.cpp
1/*
2
3Copyright (c) 2005-2024, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include <fstream>
37
38// Must be included before any other serialization headers
40#include "CardiacSimulationArchiver.hpp"
41
42#include "Exception.hpp"
43#include "ArchiveOpener.hpp"
44#include "OutputFileHandler.hpp"
45#include "ArchiveLocationInfo.hpp"
46#include "DistributedVectorFactory.hpp"
47#include "PetscTools.hpp"
48#include "FileFinder.hpp"
49
50#include "MonodomainProblem.hpp"
51#include "BidomainProblem.hpp"
52#include "BidomainWithBathProblem.hpp"
53
54template<class PROBLEM_CLASS>
55void CardiacSimulationArchiver<PROBLEM_CLASS>::Save(PROBLEM_CLASS& rSimulationToArchive,
56 const std::string& rDirectory,
57 bool clearDirectory)
58{
59 // Clear directory if requested (and make sure it exists)
60 OutputFileHandler handler(rDirectory, clearDirectory);
61
62 // Nest the archive writing, so the ArchiveOpener goes out of scope before
63 // the method ends.
64 {
65 // Open the archive files
67 ArchiveOpener<boost::archive::text_oarchive, std::ofstream> archive_opener(dir, "archive.arch");
68 boost::archive::text_oarchive* p_main_archive = archive_opener.GetCommonArchive();
69
70 // And save
71 PROBLEM_CLASS* const p_simulation_to_archive = &rSimulationToArchive;
72 (*p_main_archive) & p_simulation_to_archive;
73 }
74
75 // Write the info file
77 {
78 std::string info_path = handler.GetOutputDirectoryFullPath() + "archive.info";
79 std::ofstream info_file(info_path.c_str());
80 if (!info_file.is_open())
81 {
82 // Avoid deadlock...
84 EXCEPTION("Unable to open archive information file: " + info_path);
85 }
87 unsigned archive_version = 0; // Note that Boost version numbers are per-class; this only needs to change if we change the Load/Save methods here
88 info_file << PetscTools::GetNumProcs() << " " << archive_version;
89 }
90 else
91 {
92 bool master_threw = PetscTools::ReplicateBool(false);
93 if (master_threw)
94 {
95 EXCEPTION("Unable to open archive information file");
96 }
97 }
98 // Make sure everything is written before any process continues.
99 PetscTools::Barrier("CardiacSimulationArchiver::Save");
100}
101
102template<class PROBLEM_CLASS>
103PROBLEM_CLASS* CardiacSimulationArchiver<PROBLEM_CLASS>::Load(const std::string& rDirectory)
104{
105 FileFinder directory(rDirectory, RelativeTo::ChasteTestOutput);
107}
108
109template<class PROBLEM_CLASS>
114
115
116template<class PROBLEM_CLASS>
118{
119 // Check the directory exists
120 std::string dir_path = rDirectory.GetAbsolutePath();
121 if (!rDirectory.IsDir() || !rDirectory.Exists())
122 {
123 EXCEPTION("Checkpoint directory does not exist: " + dir_path);
124 }
125 assert(*(dir_path.end()-1) == '/'); // Paranoia
126
127 // Load the info file
128 std::string info_path = dir_path + "archive.info";
129 std::ifstream info_file(info_path.c_str());
130 if (!info_file.is_open())
131 {
132 EXCEPTION("Unable to open archive information file: " + info_path);
133 }
134 unsigned num_procs, archive_version;
135 info_file >> num_procs >> archive_version;
136
137 PROBLEM_CLASS *p_unarchived_simulation = NULL; // Shouldn't be necessary but is on some setups!
138
139 // Avoid the DistributedVectorFactory throwing a 'wrong number of processes' exception when loading,
140 // and make it get the original DistributedVectorFactory from the archive so we can compare against
141 // num_procs.
143 // Put what follows in a try-catch to make sure we reset this
144 try
145 {
146 // Figure out which process-specific archive to load first. If we're loading on the same number of
147 // processes, we must load our own one, or the mesh gets confused. Otherwise, start with 0 to make
148 // sure it exists.
149 unsigned initial_archive = num_procs == PetscTools::GetNumProcs() ? PetscTools::GetMyRank() : 0u;
150
151 // Load the master and initial process-specific archive files.
152 // This will also set up ArchiveLocationInfo for us.
153 ArchiveOpener<boost::archive::text_iarchive, std::ifstream> archive_opener(rDirectory, "archive.arch", initial_archive);
154 boost::archive::text_iarchive* p_main_archive = archive_opener.GetCommonArchive();
155 (*p_main_archive) >> p_unarchived_simulation;
156
157 // Work out how many more process-specific files to load
158 DistributedVectorFactory* p_factory = p_unarchived_simulation->rGetMesh().GetDistributedVectorFactory();
159 assert(p_factory != NULL);
160 unsigned original_num_procs = p_factory->GetOriginalFactory()->GetNumProcs();
161 assert(original_num_procs == num_procs); // Paranoia
162
163 // Merge in the extra data
164 for (unsigned archive_num=0; archive_num<original_num_procs; archive_num++)
165 {
166 if (archive_num != initial_archive)
167 {
168 std::string archive_path = ArchiveLocationInfo::GetProcessUniqueFilePath("archive.arch", archive_num);
169 std::ifstream ifs(archive_path.c_str());
170 boost::archive::text_iarchive archive(ifs);
171 p_unarchived_simulation->LoadExtraArchive(archive, archive_version);
172 }
173 }
174 }
175 catch (Exception &e)
176 {
178 if (p_unarchived_simulation)
179 {
180 delete p_unarchived_simulation;
181 }
182 throw e;
183 }
184
185 // Done.
187 return p_unarchived_simulation;
188}
189
190// Explicit instantiation
194
198
#define EXCEPTION(message)
static std::string GetProcessUniqueFilePath(const std::string &rFileName, unsigned procId=PetscTools::GetMyRank())
Archive * GetCommonArchive()
static PROBLEM_CLASS * Migrate(const FileFinder &rDirectory)
static PROBLEM_CLASS * Load(const std::string &rDirectory)
static void Save(PROBLEM_CLASS &rSimulationToArchive, const std::string &rDirectory, bool clearDirectory=true)
static void SetCheckNumberOfProcessesOnLoad(bool checkNumberOfProcessesOnLoad=true)
DistributedVectorFactory * GetOriginalFactory()
std::string GetAbsolutePath() const
bool IsDir() const
bool Exists() const
std::string GetOutputDirectoryFullPath() const
static bool ReplicateBool(bool flag)
static bool AmMaster()
static void Barrier(const std::string callerId="")
static unsigned GetMyRank()
static unsigned GetNumProcs()