1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 Post-processor utility required for working with multiSolver-enabled
30 applications. These applications store data output in a different
31 location than usual. This utility loads and unloads data from that
32 location to where post-processors expect it.
37 \*---------------------------------------------------------------------------*/
40 #include "multiSolver.H"
44 wordList * ptrSolverDomains,
45 labelList * ptrSuperLoops,
49 IStringStream optionsStream(options);
50 label nSolverDomains(0);
53 // Get solverDomainNames, if any
54 while (not optionsStream.eof())
56 token nextOption(optionsStream);
59 if (nextOption.type() == token::ERROR)
64 if (nextOption.isLabel())
66 ptrSuperLoops->setSize(++nSuperLoops);
67 ptrSuperLoops->operator[](nSuperLoops - 1)
68 = nextOption.labelToken();
71 if (nextOption.isWord())
73 ptrSolverDomains->setSize(++nSolverDomains);
74 ptrSolverDomains->operator[](nSolverDomains - 1)
75 = nextOption.wordToken();
79 // not word, not label, fail
80 FatalErrorIn("multiSolver::parseOptions")
81 << "Expecting word or label. Neither found at position "
82 << nSolverDomains - 1 << " in " << options
88 while (not optionsStream.eof())
90 token nextOption(optionsStream);
93 if (nextOption.type() == token::ERROR)
98 if (nextOption.isLabel())
100 ptrSuperLoops->setSize(++nSuperLoops);
101 ptrSuperLoops->operator[](nSuperLoops - 1)
102 = nextOption.labelToken();
104 else if (nSuperLoops > 0)
106 // might be a range -> label : label
108 if (nextOption.isPunctuation())
110 token::punctuationToken p(nextOption.pToken());
111 if (p == token::COLON)
113 token nextNextOption(optionsStream);
114 if (nextNextOption.isLabel())
116 label toValue(nextNextOption.labelToken());
119 ptrSuperLoops->operator[](nSuperLoops - 1)
122 if (toValue > fromValue)
124 // correct range format
125 for (label i = fromValue + 1; i <= toValue; i++)
127 ptrSuperLoops->setSize(++nSuperLoops);
128 ptrSuperLoops->operator[](nSuperLoops - 1) = i;
133 // greater than / less than, range
134 FatalErrorIn("multiSolver::parseOptions")
135 << "superLoop range incorrect order. 'from : "
136 << "to' where 'from' should be less than "
137 << "'to'. Values read are '" << fromValue
139 << abort(FatalError);
144 // nextNext not label
145 FatalErrorIn("multiSolver::parseOptions")
146 << "Incorrect syntax. Expecting label after ':' "
148 << abort(FatalError);
154 FatalErrorIn("multiSolver::parseOptions")
155 << "Incorrect syntax. Expecting label, word, or ':' "
157 << abort(FatalError);
163 FatalErrorIn("multiSolver::parseOptions")
164 << "Incorrect syntax. Expecting label, word, or ':' "
166 << abort(FatalError);
171 // not label, not word
172 FatalErrorIn("multiSolver::parseOptions")
173 << "Incorrect syntax. Expecting label, word, or ':' "
175 << abort(FatalError);
181 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
183 int main(int argc, char *argv[])
185 argList::validOptions.insert("list","");
186 argList::validOptions.insert
188 "load","<[solverDomainName] [superLoopNumber(s)]>"
190 argList::validOptions.insert
192 "purge","<[solverDomainName] [superLoopNumber(s)]>"
194 argList::validOptions.insert("set","<solverDomainName>");
195 argList::validOptions.insert("preDecompose", "");
196 argList::validOptions.insert("postDecompose", "");
197 argList::validOptions.insert("preReconstruct", "");
198 argList::validOptions.insert("postReconstruct", "");
200 argList::validOptions.insert("global","");
201 argList::validOptions.insert("local","");
203 // default behaviour is purge the case/[time] directory before '-load'
204 // command. '-noPurge' prevents this. Allows for more complicated
205 // load data selections by executing multiSolver several times
206 argList::validOptions.insert("noPurge","");
208 // default behaviour is: if there is only one solverDomain specified, use
209 // setSolverDomain() on it. Same as multiSolver -set solverDomain.
210 // '-noSet' prevents this.
211 argList::validOptions.insert("noSet","");
213 // default behaviour is: if there are storeFields defined, when loading, it
214 // will copy the store fields into every time instance where they are
215 // absent. '-noStore' will prevent this.
216 argList::validOptions.insert("noStore","");
218 # include "setRootCase.H"
237 bool noPurge = false;
239 bool noStore = false;
243 if (args.optionFound("list"))
248 if (args.optionFound("load"))
252 options = args.options()["load"];
254 if (args.optionFound("purge"))
258 options = args.options()["purge"];
260 if (args.optionFound("set"))
264 options = args.options()["set"];
266 if (args.optionFound("preDecompose"))
269 command = preDecompose;
271 if (args.optionFound("postDecompose"))
274 command = postDecompose;
276 if (args.optionFound("preReconstruct"))
279 command = preReconstruct;
281 if (args.optionFound("postReconstruct"))
284 command = postReconstruct;
286 if (args.optionFound("global"))
290 if (args.optionFound("local"))
294 if (args.optionFound("noPurge"))
298 if (args.optionFound("noSet"))
302 if (args.optionFound("noStore"))
310 FatalErrorIn("multiSolver::main")
311 << "multiSolver - nothing to do. Use 'multiSolver -help' for assistance."
312 << abort(FatalError);
314 else if (nCommands > 1)
316 FatalErrorIn("multiSolver::main")
317 << "More than one command found. Use only one of:"
321 << "\n\t-preDecompose"
322 << "\n\t-postDecompose"
323 << "\n\t-preReconstruct"
324 << "\n\t-postReconstruct\n"
325 << abort(FatalError);
329 FatalErrorIn("multiSolver::main")
330 << "Options global and local both specified. Use only one or "
332 << abort(FatalError);
334 if ((command != load) && (noPurge || noSet || noStore))
336 FatalErrorIn("multiSolver::main")
337 << "'noPurge', 'noSet' and 'noStore' can only be used with the "
338 << "'-load' command."
339 << abort(FatalError);
344 Foam::multiSolver::multiControlDictName,
349 const IOdictionary& mcd(multiRun.multiControlDict());
350 wordList solverDomains(0);
351 labelList superLoops(0);
355 && (command != preDecompose)
356 && (command != postDecompose)
357 && (command != preReconstruct)
358 && (command != postReconstruct)
361 parseOptions(&solverDomains, &superLoops, options);
364 // Special words - all, root
365 if (solverDomains.size() == 1)
367 if (solverDomains[0] == "all")
371 else if (solverDomains[0] == "root")
377 // More error checking
378 if (root && ((command == load) || (command == set)))
380 FatalErrorIn("multiSolver::main")
381 << "'root' is not a valid option with '-load' or '-set'"
382 << abort(FatalError);
384 if (all && (command == set))
386 FatalErrorIn("multiSolver::main")
387 << "'all' is not a valid option with '-set'"
388 << abort(FatalError);
390 if ((command == set) && ((solverDomains.size() > 1) || superLoops.size()))
392 FatalErrorIn("multiSolver::main")
393 << "'-set' can only have a single solverDomain name as an option."
394 << abort(FatalError);
396 if (all && superLoops.size())
398 FatalErrorIn("multiSolver::main")
399 << "'all' cannot be followed by superLoop numbers. To specify "
400 << "a superLoop range for all solverDomains, omit the solverDomain"
401 << " name entirely. e.g. multiSolver -load '0:4 6'"
402 << abort(FatalError);
404 if (root && superLoops.size())
406 FatalErrorIn("multiSolver::main")
407 << "'root' cannot be followed by superLoop numbers. 'root' refers"
408 << " to case/[time] directories. There are no superLoops here."
409 << abort(FatalError);
412 // Check for correct solverDomain names
418 && (command != preDecompose)
419 && (command != postDecompose)
420 && (command != preReconstruct)
421 && (command != postReconstruct)
424 forAll(solverDomains, i)
426 if (solverDomains[i] == "default")
428 // default not permitted
429 FatalErrorIn("multiSolver::main")
430 << "'default' is not a permitted solverDomain name."
431 << abort(FatalError);
433 if (!mcd.subDict("solverDomains").found(solverDomains[i]))
435 // Incorrect solver domain name
436 FatalErrorIn("multiSolver::main")
437 << "solverDomainName " << solverDomains[i] << "is not "
439 << abort(FatalError);
444 // Load specified timeClusterLists
445 timeClusterList tclSource(0);
450 tclSource = multiRun.readAllTimes();
453 if (tclSource[i].superLoop() == -1)
455 tclSource[i].times().clear();
458 tclSource.purgeEmpties();
465 && (command != preDecompose)
466 && (command != postDecompose)
467 && (command != preReconstruct)
468 && (command != postReconstruct)
471 // no superLoops specified - read entire solverDomains
472 forAll (solverDomains, sd)
476 multiRun.readSolverDomainTimes(solverDomains[sd])
485 && (command != preDecompose)
486 && (command != postDecompose)
487 && (command != preReconstruct)
488 && (command != postReconstruct)
491 // read individual superLoops
492 if (!solverDomains.size())
494 solverDomains = mcd.subDict("solverDomains").toc();
496 forAll(superLoops, sl)
498 forAll(solverDomains, sd)
500 if (solverDomains[sd] == "default") continue;
503 multiRun.readSuperLoopTimes
513 if (tclSource.size())
515 if (!tclSource.purgeEmpties())
517 FatalErrorIn("multiSolver::main")
518 << "No data found with specified parameters."
519 << abort(FatalError);
527 Info << "Listing available data:\n" << endl;
528 Info << "superLoops by solverDomain:" << endl;
529 solverDomains = mcd.subDict("solverDomains").toc();
532 multiRun.multiDictRegistry().path()/"multiSolver"
535 forAll(solverDomains, i)
537 if (solverDomains[i] == "default") continue;
538 Info << solverDomains[i] << ":" << endl;
539 Info << multiRun.findSuperLoops(listPath/solverDomains[i])
547 // Default behaviour - use local time unless overlapping, then use
548 // global time; if global overlaps, fail. -local and -global force
550 bool localOverlap(!multiRun.nonOverlapping(tclSource, false));
551 bool globalOverlap(!multiRun.nonOverlapping(tclSource, true));
552 if (local && localOverlap)
554 FatalErrorIn("multiSolver::main")
555 << "'-local' option used for data with overlapping local "
556 << "values. Try using a single solverDomain / superLoop, "
557 << "or leave '-local' off."
558 << abort(FatalError);
562 FatalErrorIn("multiSolver::main")
563 << "globalTime values are overlapping. This should not "
564 << "happen. Ensure you have not specified the same "
565 << "solverDomain and/or superLoop more than once. If "
566 << "that fails, try using 'multiSolver -purge all' and "
567 << "rerunning the simulation. If the problem persists, "
569 << abort(FatalError);
574 Info << "Purging existing time directories in case root"
576 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
579 Info << "Loading data from multiSolver directories to case root"
583 !multiRun.loadTimeClusterList
586 global || localOverlap,
591 FatalErrorIn("multiRun::main")
592 << "loadTimeClusterList failed. timeClusterList contents: "
594 << abort(FatalError);
601 Info << "Purging time directories from case root" << endl;
602 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
606 Info << "Purging time directories from multiSolver directories"
610 // do not purge 'initial' directory, even if specified
611 if (tclSource[i].superLoop() < 0) continue;
614 multiRun.findInstancePath(tclSource[i], 0).path()
625 Info << "Performing preDecompose" << endl;
626 multiRun.preCondition();
631 Info << "Performing postDecompose" << endl;
633 fileNameList dirEntries
637 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
641 forAll(dirEntries, de)
643 if (dirEntries[de](9) == "processor")
645 Info << "Reading " << dirEntries[de] << endl;
647 multiRun.postCondition
652 // Copy system to processorN
655 multiRun.multiDictRegistry().path()
656 /multiRun.multiDictRegistry().system(),
657 multiRun.multiDictRegistry().path()/dirEntries[de]
660 // Copy constant/files to processorN/constant
661 fileNameList constantContents
665 multiRun.multiDictRegistry().path()
666 /multiRun.multiDictRegistry().constant(),
670 forAll(constantContents, cc)
674 multiRun.multiDictRegistry().path()
675 /multiRun.multiDictRegistry().constant()
676 /constantContents[cc],
677 multiRun.multiDictRegistry().path()/dirEntries[de]
678 /multiRun.multiDictRegistry().constant()
682 // Copy constant/directories to processorN/constant
683 constantContents = readDir
685 multiRun.multiDictRegistry().path()
686 /multiRun.multiDictRegistry().constant(),
689 forAll(constantContents, cc)
691 // Ingore mesh directory
692 if (constantContents[cc] == "polyMesh")
698 multiRun.multiDictRegistry().path()
699 /multiRun.multiDictRegistry().constant()
700 /constantContents[cc],
701 multiRun.multiDictRegistry().path()/dirEntries[de]
702 /multiRun.multiDictRegistry().constant()
707 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
712 Info << "Performing preReconstruct" << endl;
713 fileNameList dirEntries
717 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
721 forAll(dirEntries, de)
723 if (dirEntries[de](9) == "processor")
725 Info << "Reading " << dirEntries[de] << endl;
726 multiRun.preCondition
730 // Fix missing 0.00000e+00 directory if it exists
733 multiRun.multiDictRegistry().path()/dirEntries[de]/"0"
739 case postReconstruct:
741 Info << "Performing postReconstruct" << endl;
743 Info << "Reading preconditioned time directories" << endl;
744 multiRun.postCondition();
746 Info << "Purging preconditioned time directories"
749 // Clean up extra time directories
750 fileNameList dirEntries
754 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
758 forAll(dirEntries, de)
760 if (dirEntries[de](9) == "processor")
762 multiRun.purgeTimeDirs
764 multiRun.multiDictRegistry().path()/dirEntries[de]
772 // Execute set command - either from an explicit '-set' or from a '-load'
773 // with only one solverDomain as an option
780 && (solverDomains.size() == 1)
785 Info << "Changing to " << solverDomains[0] << " settings." << endl;
786 multiRun.setSolverDomainPostProcessing(solverDomains[0]);
789 Info << "\nCommand completed successfully.\n" << endl;
793 // ************************************************************************* //