Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
ParamFile.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/* ParamFile.cc (C) 2000-2026 */
9/* */
10/* Reader of parameter files. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/launcher/internal/ParamFile.h"
15
16#include "arcane/launcher/ArcaneLauncher.h"
17
18#include "arcane/utils/PlatformUtils.h"
19#include "arcane/utils/UniqueArray.h"
20#include "arcane/utils/FatalErrorException.h"
21#include "arcane/utils/StringBuilder.h"
22#include "arcane/utils/CommandLineArguments.h"
23#include "arcane/utils/JSONReader.h"
24
25#include <iostream>
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30namespace Arcane
31{
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36/*
37 * Example of ArcaNet file (RC2) :
38{
39 "versions": {
40 "_": 0,
41 "arcane": 0,
42 "arccher": 0
43 },
44
45 // Coucou
46
47 "general": {
48 "_": {
49 "name": "Common"
50 },
51 "arcane": {
52 "dataset": "./dataset.arc",
53 "options": {
54 "//meshes/mesh/filename": "aaa.msh",
55 "T": 4
56 }
57 },
58 "arccher": {
59 "mpi": 4
60 }
61 },
62
63 "commons": {
64 "4procs": {
65 "_": {
66 "name": "4 Procs"
67 },
68 "arccher": {
69 "mpi": 4
70 }
71 },
72 "4threads": {
73 "_": {
74 "name": "4 Threads"
75 },
76 "arcane": {
77 "options": {
78 "S": 4
79 }
80 }
81 },
82 "16mpithreads": {
83 "_": {
84 "name": "16 Hybrid",
85 "depend_a": ["4procs", "4threads"]
86 }
87 }
88 },
89
90 "variations": {
91 "nb_iterations": {
92 "10": {
93 "_": {
94 "name": "10 itérations"
95 },
96 "arcane": {
97 "options": {
98 "MaxIteration": 10
99 }
100 }
101 },
102 "20": {
103 "_": {
104 "name": "20 itérations"
105 },
106 "arcane": {
107 "options": {
108 "MaxIteration": 20
109 }
110 }
111 }
112 }
113 },
114
115 "cases": {
116 // Reserved symbol for cases name : ":", "=", "~"
117
118 // How to call "case1" :
119 // - "case1" -> ok
120 "case1": {
121 "_": {
122 "name": "Cas 1",
123 // Reserved symbol for depend_a/depend_b elements : "=", "!"
124 "depend_a": ["4procs", "nb_iterations=10"]
125 },
126
127 // Reserved symbol for prog name : ":", "=", "~"
128 "arcane": {
129 "options": {
130 "//meshes/mesh/filename": "aaa.msh",
131 "T": 4
132 }
133 },
134
135 "arccher": {
136 "mpi": 2
137 }
138 },
139
140 // How to call "case2" :
141 // - "case2" -> error
142 // - "case2:nb_iterations=10" -> ok
143 // - "case2:nb_iterations=20" -> error
144 // - "case2~init:nb_iterations=10" -> ok
145 // - "case2~init:nb_iterations=20" -> error
146 "case2": {
147 "_": {
148 "name": "Cas 2",
149 "depend_b": ["16mpithreads"],
150 "depend_a": ["nb_iterations!20"]
151 },
152 "arcane": {
153 "options": {
154 "//meshes/mesh/filename": "bbb.msh",
155 "T": 8
156 }
157 },
158 "arcane~init": {
159 "options": {
160 "//meshes/mesh/filename": "bbb.msh",
161 "T": 16
162 }
163 }
164 }
165 }
166}
167 */
168
170{
171 public:
172
173 Reader(CommandLineArguments& cargs, const JSONValue& root);
174
175 public:
176
177 void read(const String& case_and_variations_values);
178 bool displayDetails() const { return m_build_path; }
179 String path() const { return m_path; }
180 String configPartName() { return m_arcane_custom_part_name.empty() ? m_arcane_part_name : m_arcane_custom_part_name; }
181
182 private:
183
184 void _readDependPart(const JSONValue& depend_part);
185 void _readConfigPart(const JSONValue& config_part, const String& node_name);
186 void _addElemInPath(const String& elem);
187 void _readFileBeforePart(const JSONValue& config_part);
188 void _readFileAfterPart(const JSONValue& config_part);
189 void _readArcanePart(const JSONValue& config_part);
190
191 private:
192
193 // StringBuilder m_name;
194 // bool m_is_name_empty = true;
195 bool m_build_path = false;
196 StringBuilder m_path;
197 Integer m_level_path = 0;
198 CommandLineArguments& m_cargs;
199 const JSONValue& m_root;
200 UniqueArray<String> m_explored;
201 UniqueArray<String> m_variations_key_resolved;
202 UniqueArray<String> m_variations_value_resolved;
203 const String m_arcane_part_name = "arcane";
204 String m_arcane_custom_part_name;
205};
206
207/*---------------------------------------------------------------------------*/
208/*---------------------------------------------------------------------------*/
209
210ParamFile::Reader::Reader(CommandLineArguments& cargs, const JSONValue& root)
211: m_build_path(!platform::getEnvironmentVariable("ARCANE_ARCANET_PATH").null())
212, m_cargs(cargs)
213, m_root(root)
214{
215}
216
217/*---------------------------------------------------------------------------*/
218/*---------------------------------------------------------------------------*/
219
220void ParamFile::Reader::
221read(const String& case_and_variations_values)
222{
223 String case_name;
224 if (!case_and_variations_values.empty()) {
225 UniqueArray<String> split_case;
226 case_and_variations_values.split(split_case, ':');
227
228 for (const String& elem : split_case) {
229 if (elem.contains("=")) {
230 UniqueArray<String> split_variation;
231 elem.split(split_variation, '=');
232 if (split_variation.size() != 2) {
233 ARCANE_FATAL("Bad element in Case option.");
234 }
235 m_variations_key_resolved.add(split_variation[0]);
236 m_variations_value_resolved.add(split_variation[1]);
237 // std::cout << "Add key=value : " << split_elem[0] << " = " << split_elem[1] << std::endl;
238 }
239 else {
240 if (!case_name.empty()) {
241 ARCANE_FATAL("You must choose a variation for '{0}'.", elem);
242 }
243 if (elem.contains("~")) {
244 UniqueArray<String> split_case_name_with_prog_subname;
245 elem.split(split_case_name_with_prog_subname, '~');
246 if (split_case_name_with_prog_subname.size() != 2) {
247 ARCANE_FATAL("Bad element in Case option.");
248 }
249 case_name = split_case_name_with_prog_subname[0];
250 m_arcane_custom_part_name = m_arcane_part_name + "~" + split_case_name_with_prog_subname[1];
251 }
252 else {
253 case_name = elem;
254 }
255 }
256 }
257 }
258
259 // "general" part
260 {
261 const JSONValue cases = m_root.child("general");
262 if (!cases.isNull()) {
263 // std::cout << "General part" << std::endl;
264 m_explored.add("general");
265 _readConfigPart(cases, "general");
266 }
267 }
268 if (!case_name.empty()) {
269 const JSONValue cases = m_root.child("cases").child(case_name);
270 if (cases.null()) {
271 ARCANE_FATAL("Case '{0}' not found.", case_name);
272 }
273 // std::cout << "Case part : " << case_clean << std::endl;
274 m_explored.add(case_name);
275 _readConfigPart(cases, case_name);
276 }
277}
278
279/*---------------------------------------------------------------------------*/
280/*---------------------------------------------------------------------------*/
281
282void ParamFile::Reader::
283_readDependPart(const JSONValue& depend_part)
284{
285 if (depend_part.null())
286 return;
287
288 const JSONValueList array = depend_part.valueAsArray();
289 for (const JSONValue elem : array) {
290 JSONValue dependence_node;
291 String dependence_name = elem.valueAsStringView();
292 {
293 if (dependence_name.contains("=")) {
294 UniqueArray<String> split_variation;
295 dependence_name.split(split_variation, '=');
296 if (split_variation.size() != 2) {
297 ARCANE_FATAL("Element '{0}' is invalid.", dependence_name);
298 }
299
300 const JSONValue variation = m_root.child("variations").child(split_variation[0]).child(split_variation[1]);
301 if (variation.null()) {
302 ARCANE_FATAL("Element '//variations/{0}/{1}' not found.", split_variation[0], split_variation[1]);
303 }
304 if (m_explored.contains(dependence_name)) {
305 ARCANE_FATAL("Element '//variations/{0}/{1}' already explored.", split_variation[0], split_variation[1]);
306 }
307 m_explored.add(dependence_name);
308 }
309 else {
310 dependence_node = m_root.child("commons").child(dependence_name);
311 if (!dependence_node.null()) {
312 if (m_explored.contains(dependence_name)) {
313 ARCANE_FATAL("Element '//commons/{0}' already explored.", dependence_name);
314 }
315 m_explored.add(dependence_name);
316 }
317 else {
318 String cleaned_dependence_name;
319 UniqueArray<String> split_dependence_name;
320 if (dependence_name.contains("!")) {
321 dependence_name.split(split_dependence_name, '!');
322 cleaned_dependence_name = split_dependence_name[0];
323 }
324 else {
325 cleaned_dependence_name = dependence_name;
326 }
327 auto pos_elem = m_variations_key_resolved.span().findFirst(cleaned_dependence_name);
328
329 if (!pos_elem.has_value()) {
330 const JSONValue error_ = m_root.child("variations").child(cleaned_dependence_name);
331 if (error_.null()) {
332 ARCANE_FATAL("Element '//commons/{0}' not found.", cleaned_dependence_name);
333 }
334 ARCANE_FATAL("Element '{0}' not found in 'Case' cmd line option.", cleaned_dependence_name);
335 }
336
337 String value = m_variations_value_resolved[pos_elem.value()];
338 if (!split_dependence_name.empty()) {
339 ArrayView excluded_values(split_dependence_name.subView(1, split_dependence_name.size() - 1));
340 if (excluded_values.contains(value)) {
341 ARCANE_FATAL("Element '//variations/{0}/{1}' is excluded.", cleaned_dependence_name, value);
342 }
343 }
344 dependence_name = cleaned_dependence_name + "=" + value;
345
346 dependence_node = m_root.child("variations").child(cleaned_dependence_name).child(value);
347 if (dependence_node.null()) {
348 ARCANE_FATAL("Element '//variations/{0}/{1}' not found.", cleaned_dependence_name, value);
349 }
350 if (m_explored.contains(dependence_name)) {
351 ARCANE_FATAL("Element '//variations/{0}/{1}' already explored.", cleaned_dependence_name, value);
352 }
353 m_explored.add(dependence_name);
354 }
355 }
356 }
357 _readConfigPart(dependence_node, dependence_name);
358 }
359}
360
361/*---------------------------------------------------------------------------*/
362/*---------------------------------------------------------------------------*/
363
364void ParamFile::Reader::
365_readConfigPart(const JSONValue& config_part, const String& node_name)
366{
367 if (m_build_path) {
368 _addElemInPath("{");
369 m_level_path++;
370 _readFileBeforePart(config_part);
371 _addElemInPath(node_name);
372 _readArcanePart(config_part);
373 _readFileAfterPart(config_part);
374 m_level_path--;
375 _addElemInPath("}");
376 }
377 else {
378 _readFileBeforePart(config_part);
379 _readArcanePart(config_part);
380 _readFileAfterPart(config_part);
381 }
382}
383
384/*---------------------------------------------------------------------------*/
385/*---------------------------------------------------------------------------*/
386
387void ParamFile::Reader::
388_addElemInPath(const String& elem)
389{
390 m_path += "\n";
391 for (Integer i = 0; i < m_level_path; ++i)
392 m_path += " ";
393 m_path += elem;
394}
395
396/*---------------------------------------------------------------------------*/
397/*---------------------------------------------------------------------------*/
398
399void ParamFile::Reader::
400_readFileBeforePart(const JSONValue& config_part)
401{
402 const JSONValue depend_before_var = config_part.child("_").child("depend_b");
403 _readDependPart(depend_before_var);
404
405 // const JSONValue name_var = config_part.child("_").child("name");
406 // if (!name_var.isNull()) {
407 // if (m_is_name_empty)
408 // m_is_name_empty = false;
409 // else
410 // m_name += " -> ";
411 //
412 // m_name += "\"";
413 // m_name += name_var.valueAsStringView();
414 // m_name += "\"";
415 //
416 // std::cout << "Config part: " << name_var.valueAsStringView() << std::endl;
417 // }
418 // else {
419 // std::cout << "Config part" << std::endl;
420 // }
421}
422
423/*---------------------------------------------------------------------------*/
424/*---------------------------------------------------------------------------*/
425
426void ParamFile::Reader::
427_readFileAfterPart(const JSONValue& config_part)
428{
429 const JSONValue depend_after_var = config_part.child("_").child("depend_a");
430 _readDependPart(depend_after_var);
431}
432
433/*---------------------------------------------------------------------------*/
434/*---------------------------------------------------------------------------*/
435
436void ParamFile::Reader::
437_readArcanePart(const JSONValue& config_part)
438{
439 JSONValue arcane_part;
440 if (!m_arcane_custom_part_name.empty()) {
441 arcane_part = config_part.child(m_arcane_custom_part_name);
442 }
443 if (arcane_part.null()) {
444 arcane_part = config_part.child(m_arcane_part_name);
445 }
446 else if (m_build_path) {
447 m_path += "(";
448 m_path += m_arcane_custom_part_name;
449 m_path += ")";
450 // m_name += "(";
451 // m_name += m_arcane_custom_part_name;
452 // m_name += ")";
453 }
454
455 const JSONValue dataset = arcane_part.child("dataset");
456 if (!dataset.isNull()) {
457 m_cargs.addParameterLine(String::format("CaseDatasetFileName={0}", dataset.value()));
458 //std::cout << "Dataset : " << dataset.value() << std::endl;
459 }
460
461 const JSONValue arcane_params = arcane_part.child("options");
462
463 if (!arcane_params.isNull()) {
464 const JSONKeyValueList params = arcane_params.keyValueChildren();
465
466 for (auto elem : params) {
467 m_cargs.addParameterLine(String::format("{0}={1}", elem.name(), elem.value().valueAsStringView()));
468
469 // std::cout << "Elem : " << elem.name() << " -- val : " << elem.value().valueAsStringView() << std::endl;
470 }
471 }
472}
473
474/*---------------------------------------------------------------------------*/
475/*---------------------------------------------------------------------------*/
476
477/*---------------------------------------------------------------------------*/
478/*---------------------------------------------------------------------------*/
479
480void ParamFile::
481editParams(const String& param_file_name, const String& variation)
482{
483 CommandLineArguments cargs = ArcaneLauncher::applicationInfo().commandLineArguments();
484
485 JSONDocument json_doc;
486 {
487 UniqueArray<Byte> bytes;
488
489 if (platform::readAllFile(param_file_name, false, bytes)) {
490 ARCANE_FATAL("Param file not available");
491 }
492
493 json_doc.parse(bytes, param_file_name, (JSONDocument::ParseCommentsFlag | JSONDocument::ParseNumbersAsStringsFlag));
494 }
495
496 const JSONValue root = json_doc.root();
497 Reader reader(cargs, root);
498
499 reader.read(variation);
500
501 if (reader.displayDetails()) {
502 std::cout << "ArcaNet path: " << reader.path() << std::endl;
503 std::cout << "ArcaNet config part readed: " << reader.configPartName() << std::endl;
504 }
505
506 // StringList names;
507 // StringList values;
508 // cargs.fillParameters(names, values);
509 // for (Integer i = 0, n = names.count(); i < n; ++i) {
510 // std::cout << "Final Elem : " << names[i] << " -- val : " << values[i] << std::endl;
511 // }
512
514}
515
516/*---------------------------------------------------------------------------*/
517/*---------------------------------------------------------------------------*/
518
519} // End namespace Arcane
520
521/*---------------------------------------------------------------------------*/
522/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Integer size() const
Number of elements in the vector.
void setCommandLineArguments(const CommandLineArguments &args)
Sets the command line arguments.
const CommandLineArguments & commandLineArguments() const
Command line arguments.
static ApplicationInfo & applicationInfo()
Application information.
void add(ConstReferenceType val)
Adds element val to the end of the array.
@ ParseNumbersAsStringsFlag
Parse all numbers (ints/doubles) as strings.
@ ParseCommentsFlag
Allow one-line (//) and multi-line (/‍**/) comments.
Unicode character string constructor.
bool empty() const
True if the string is empty (null or "").
Definition String.cc:317
void split(StringContainer &str_array, char c) const
Splits the string based on the character c.
1D data vector with value semantics (STL style).
Namespace for platform-dependent functions.
bool readAllFile(StringView filename, bool is_binary, ByteArray &out_bytes)
Reads the content of a file and stores it in out_bytes.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
GenericReader< UTF8<>, UTF8<> > Reader
Reader with UTF8 encoding and default allocator.
Definition reader.h:2234