Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
BasicWriter.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* BasicWriter.cc (C) 2000-2025 */
9/* */
10/* Simple writing for checkpoints/restorations. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/std/internal/BasicWriter.h"
15
16#include "arcane/utils/FatalErrorException.h"
17#include "arcane/utils/StringBuilder.h"
18#include "arcane/utils/PlatformUtils.h"
19#include "arcane/utils/JSONWriter.h"
20#include "arcane/utils/IDataCompressor.h"
21#include "arcane/utils/MemoryView.h"
22#include "arcane/utils/Ref.h"
23#include "arcane/utils/IHashAlgorithm.h"
24
25#include "arcane/core/IParallelMng.h"
26#include "arcane/core/ItemGroup.h"
27#include "arcane/core/IVariable.h"
28#include "arcane/core/IItemFamily.h"
29#include "arcane/core/IData.h"
30#include "arcane/core/internal/IVariableInternal.h"
31
32#include "arcane/std/internal/ParallelDataWriter.h"
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37namespace Arcane::impl
38{
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43BasicWriter::
44BasicWriter(IApplication* app, IParallelMng* pm, const String& path,
45 eOpenMode open_mode, Int32 version, bool want_parallel)
46: BasicReaderWriterCommon(app, pm, path, open_mode)
47, m_want_parallel(want_parallel)
48, m_version(version)
49{
50}
51
52/*---------------------------------------------------------------------------*/
53/*---------------------------------------------------------------------------*/
54
55void BasicWriter::
56initialize()
57{
58 _checkNoInit();
59
60 Int32 rank = m_parallel_mng->commRank();
61 if (m_open_mode == OpenModeTruncate && m_parallel_mng->isMasterIO())
62 platform::recursiveCreateDirectory(m_path);
63 m_parallel_mng->barrier();
64 String filename = _getBasicVariableFile(m_version, m_path, rank);
65 m_text_writer = makeRef(new KeyValueTextWriter(traceMng(), filename, m_version));
66 m_text_writer->setDataCompressor(m_data_compressor);
67 m_text_writer->setHashAlgorithm(m_hash_algorithm);
68
69 // Allows overriding the compression service used by an environment variable
70 // if none is set
71 if (!m_data_compressor.get()) {
72 String data_compressor_name = platform::getEnvironmentVariable("ARCANE_DEFLATER");
73 if (!data_compressor_name.null()) {
74 data_compressor_name = data_compressor_name + "DataCompressor";
75 auto bc = _createDeflater(m_application, data_compressor_name);
76 info() << "Use data_compressor from environment variable ARCANE_DEFLATER name=" << data_compressor_name;
77 m_data_compressor = bc;
78 m_text_writer->setDataCompressor(bc);
79 }
80 }
81
82 // Same for the hash calculation service
83 if (!m_hash_algorithm.get()) {
84 String hash_algorithm_name = platform::getEnvironmentVariable("ARCANE_HASHALGORITHM");
85 if (hash_algorithm_name.null())
86 hash_algorithm_name = "SHA3_256";
87 else
88 info() << "Use hash algorithm from environment variable ARCANE_HASHALGORITHM name=" << hash_algorithm_name;
89 hash_algorithm_name = hash_algorithm_name + "HashAlgorithm";
90 auto v = _createHashAlgorithm(m_application, hash_algorithm_name);
91 m_hash_algorithm = v;
92 m_text_writer->setHashAlgorithm(v);
93 }
94
95 // For testing, allows specifying a service for global hash calculation.
96 if (!m_compare_hash_algorithm.get()) {
97 String algo_name = platform::getEnvironmentVariable("ARCANE_COMPAREHASHALGORITHM");
98 if (!algo_name.empty()) {
99 info() << "Use global hash algorithm from environment variable ARCANE_COMPAREHASHALGORITHM name=" << algo_name;
100 algo_name = algo_name + "HashAlgorithm";
101 auto v = _createHashAlgorithm(m_application, algo_name);
102 m_compare_hash_algorithm = v;
103 }
104 }
105
106 m_global_writer = new BasicGenericWriter(m_application, m_version, m_text_writer);
107 if (m_verbose_level > 0)
108 info() << "** OPEN MODE = " << m_open_mode;
109}
110
111/*---------------------------------------------------------------------------*/
112/*---------------------------------------------------------------------------*/
113
114void BasicWriter::
115_checkNoInit()
116{
117 if (m_is_init)
118 ARCANE_FATAL("initialize() has already been called");
119}
120
121/*---------------------------------------------------------------------------*/
122/*---------------------------------------------------------------------------*/
123
124Ref<ParallelDataWriter> BasicWriter::
125_getWriter(IVariable* var)
126{
127 return m_parallel_data_writers.getOrCreateWriter(var->itemGroup());
128}
129
130/*---------------------------------------------------------------------------*/
131/*---------------------------------------------------------------------------*/
132
133void BasicWriter::
134_directWriteVal(IVariable* var, IData* data)
135{
136 info(4) << "DIRECT WRITE VAL v=" << var->fullName();
137
138 IData* write_data = data;
139 Int64ConstArrayView written_unique_ids;
140 Int64UniqueArray wanted_unique_ids;
141 Int64UniqueArray sequential_written_unique_ids;
142
143 Ref<IData> allocated_write_data;
144 const bool is_mesh_variable = (var->itemKind() != IK_Unknown);
145 if (is_mesh_variable) {
146 ItemGroup group = var->itemGroup();
147 if (m_want_parallel) {
148 Ref<ParallelDataWriter> writer = _getWriter(var);
149 written_unique_ids = writer->sortedUniqueIds();
150 allocated_write_data = writer->getSortedValues(data);
151 write_data = allocated_write_data.get();
152 }
153 else {
154 // TODO check that uniqueId() are sorted.
155 // Normally it is always the case.
156 _fillUniqueIds(group, sequential_written_unique_ids);
157 written_unique_ids = sequential_written_unique_ids.view();
158 }
159 // Writes the group information if this is the first time accessing this group.
160 if (m_written_groups.find(group) == m_written_groups.end()) {
161 info(5) << "WRITE GROUP " << group.name();
162 const IItemFamily* item_family = group.itemFamily();
163 const String& gname = group.name();
164 String group_full_name = item_family->fullName() + "_" + gname;
165 _fillUniqueIds(group, wanted_unique_ids);
166 if (m_is_save_values)
167 m_global_writer->writeItemGroup(group_full_name, written_unique_ids, wanted_unique_ids.view());
168 m_written_groups.insert(group);
169 }
170 }
171
172 Ref<ISerializedData> sdata(write_data->createSerializedDataRef(false));
173 String compare_hash;
174 if (is_mesh_variable) {
175 compare_hash = _computeCompareHash(var, write_data);
176 }
177 m_global_writer->writeData(var->fullName(), sdata.get(), compare_hash, m_is_save_values);
178}
179
180/*---------------------------------------------------------------------------*/
181/*---------------------------------------------------------------------------*/
193String BasicWriter::
194_computeCompareHash(IVariable* var, IData* write_data)
195{
196 IHashAlgorithm* hash_algo = m_compare_hash_algorithm.get();
197 if (!hash_algo)
198 return {};
199 return var->_internalApi()->computeComparisonHashCollective(hash_algo, write_data);
200}
201
202/*---------------------------------------------------------------------------*/
203/*---------------------------------------------------------------------------*/
204
205void BasicWriter::
206write(IVariable* var, IData* data)
207{
208 if (var->isPartial()) {
209 info() << "** WARNING: partial variable not implemented in BasicWriter";
210 return;
211 }
212 _directWriteVal(var, data);
213}
214
215/*---------------------------------------------------------------------------*/
216/*---------------------------------------------------------------------------*/
217
218void BasicWriter::
219setMetaData(const String& meta_data)
220{
221 // In version 3, checkpoint metadata is in the database.
222 if (m_version >= 3) {
223 Span<const Byte> bytes = meta_data.utf8();
224 Int64 length = bytes.length();
225 String key_name = "Global:CheckpointMetadata";
226 m_text_writer->setExtents(key_name, Int64ConstArrayView(1, &length));
227 m_text_writer->write(key_name, asBytes(bytes));
228 }
229 else {
230 Int32 my_rank = m_parallel_mng->commRank();
231 String filename = _getMetaDataFileName(my_rank);
232 std::ofstream ofile(filename.localstr(), ios::binary);
233 meta_data.writeBytes(ofile);
234 }
235}
236
237/*---------------------------------------------------------------------------*/
238/*---------------------------------------------------------------------------*/
239
240void BasicWriter::
241beginWrite(const VariableCollection& vars)
242{
243 ARCANE_UNUSED(vars);
244 Int32 my_rank = m_parallel_mng->commRank();
245 m_global_writer->initialize(m_path, my_rank);
246}
247
248/*---------------------------------------------------------------------------*/
249/*---------------------------------------------------------------------------*/
250
251void BasicWriter::
252_endWriteV3()
253{
254 const Int64 nb_part = m_parallel_mng->commSize();
255
256 // Saves the information in JSON format
257 JSONWriter jsw;
258
259 {
260 JSONWriter::Object main_object(jsw);
261 jsw.writeKey(_getArcaneDBTag());
262 {
263 JSONWriter::Object db_object(jsw);
264 jsw.write("Version", (Int64)m_version);
265 jsw.write("NbPart", nb_part);
266 jsw.write("HasValues", m_is_save_values);
267
268 String data_compressor_name;
269 Int64 data_compressor_min_size = 0;
270 if (m_data_compressor.get()) {
271 data_compressor_name = m_data_compressor->name();
272 data_compressor_min_size = m_data_compressor->minCompressSize();
273 }
274 jsw.write("DataCompressor", data_compressor_name);
275 jsw.write("DataCompressorMinSize", String::fromNumber(data_compressor_min_size));
276
277 // Saves the hash algorithm name
278 {
279 String name;
280 if (m_hash_algorithm.get())
281 name = m_hash_algorithm->name();
282 jsw.write("HashAlgorithm", name);
283 }
284
285 // Saves the hash algorithm name for comparisons
286 {
287 String name;
288 if (m_compare_hash_algorithm.get())
289 name = m_compare_hash_algorithm->name();
290 jsw.write("ComparisonHashAlgorithm", name);
291 }
292 }
293 }
294
295 StringBuilder filename = m_path;
296 filename += "/arcane_acr_db.json";
297 String fn = filename.toString();
298 std::ofstream ofile(fn.localstr());
299 ofile << jsw.getBuffer();
300}
301
302/*---------------------------------------------------------------------------*/
303/*---------------------------------------------------------------------------*/
304
305void BasicWriter::
306endWrite()
307{
308 const IParallelMng* pm = m_parallel_mng;
309 if (pm->isMasterIO()) {
310 if (m_version >= 3) {
311 _endWriteV3();
312 }
313 else {
314 Int64 nb_part = pm->commSize();
315 StringBuilder filename = m_path;
316 filename += "/infos.txt";
317 String fn = filename.toString();
318 std::ofstream ofile(fn.localstr());
319 ofile << nb_part << '\n';
320 }
321 }
322 m_global_writer->endWrite();
323}
324
325/*---------------------------------------------------------------------------*/
326/*---------------------------------------------------------------------------*/
327
328} // namespace Arcane::impl
329
330/*---------------------------------------------------------------------------*/
331/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Interface of a data item.
Definition IData.h:34
Interface of a hashing algorithm.
virtual String computeComparisonHashCollective(IHashAlgorithm *hash_algo, IData *sorted_data)=0
Calculates the comparison hash for the variable.
Interface of a variable.
Definition IVariable.h:40
virtual bool isPartial() const =0
Indicates if the variable is partial.
virtual IVariableInternal * _internalApi()=0
Internal Arcane API.
View of an array of elements of type T.
Definition Span.h:635
void writeBytes(std::ostream &o) const
Writes the string in UTF-8 format to the stream o.
Definition String.cc:1247
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
ByteConstArrayView utf8() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:277
TraceMessage info() const
Flow for an information message.
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
std::int64_t Int64
Signed integer type of 64 bits.
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
Impl::SpanTypeFromSize< conststd::byte, SizeType >::SpanType asBytes(const SpanImpl< DataType, SizeType, Extent > &s)
Converts the view into an array of non-modifiable bytes.
Definition Span.h:1032
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
std::int32_t Int32
Signed integer type of 32 bits.