1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
5 \\ / A nd | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
28 Post-processor utility required for working with multiSolver-enabled
29 applications. These applications store data output in a different
30 location than usual. This utility loads and unloads data from that
31 location to where post-processors expect it.
36 \*---------------------------------------------------------------------------*/
39 #include "multiSolver.H"
43 wordList * ptrSolverDomains,
44 labelList * ptrSuperLoops,
48 IStringStream optionsStream(options);
49 label nSolverDomains(0);
52 // Get solverDomainNames, if any
53 while (not optionsStream.eof())
55 token nextOption(optionsStream);
58 if (nextOption.type() == token::ERROR)
63 if (nextOption.isLabel())
65 ptrSuperLoops->setSize(++nSuperLoops);
66 ptrSuperLoops->operator[](nSuperLoops - 1)
67 = nextOption.labelToken();
70 if (nextOption.isWord())
72 ptrSolverDomains->setSize(++nSolverDomains);
73 ptrSolverDomains->operator[](nSolverDomains - 1)
74 = nextOption.wordToken();
78 // not word, not label, fail
79 FatalErrorIn("multiSolver::parseOptions")
80 << "Expecting word or label. Neither found at position "
81 << nSolverDomains - 1 << " in " << options
87 while (not optionsStream.eof())
89 token nextOption(optionsStream);
92 if (nextOption.type() == token::ERROR)
97 if (nextOption.isLabel())
99 ptrSuperLoops->setSize(++nSuperLoops);
100 ptrSuperLoops->operator[](nSuperLoops - 1)
101 = nextOption.labelToken();
103 else if (nSuperLoops > 0)
105 // might be a range -> label : label
107 if (nextOption.isPunctuation())
109 token::punctuationToken p(nextOption.pToken());
110 if (p == token::COLON)
112 token nextNextOption(optionsStream);
113 if (nextNextOption.isLabel())
115 label toValue(nextNextOption.labelToken());
118 ptrSuperLoops->operator[](nSuperLoops - 1)
121 if (toValue > fromValue)
123 // correct range format
124 for (label i = fromValue + 1; i <= toValue; i++)
126 ptrSuperLoops->setSize(++nSuperLoops);
127 ptrSuperLoops->operator[](nSuperLoops - 1) = i;
132 // greater than / less than, range
133 FatalErrorIn("multiSolver::parseOptions")
134 << "superLoop range incorrect order. 'from : "
135 << "to' where 'from' should be less than "
136 << "'to'. Values read are '" << fromValue
138 << abort(FatalError);
143 // nextNext not label
144 FatalErrorIn("multiSolver::parseOptions")
145 << "Incorrect syntax. Expecting label after ':' "
147 << abort(FatalError);
153 FatalErrorIn("multiSolver::parseOptions")
154 << "Incorrect syntax. Expecting label, word, or ':' "
156 << abort(FatalError);
162 FatalErrorIn("multiSolver::parseOptions")
163 << "Incorrect syntax. Expecting label, word, or ':' "
165 << abort(FatalError);
170 // not label, not word
171 FatalErrorIn("multiSolver::parseOptions")
172 << "Incorrect syntax. Expecting label, word, or ':' "
174 << abort(FatalError);
180 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
182 int main(int argc, char *argv[])
184 argList::validOptions.insert("list","");
185 argList::validOptions.insert
187 "load","<[solverDomainName] [superLoopNumber(s)]>"
189 argList::validOptions.insert
191 "purge","<[solverDomainName] [superLoopNumber(s)]>"
193 argList::validOptions.insert("set","<solverDomainName>");
194 argList::validOptions.insert("preDecompose", "");
195 argList::validOptions.insert("postDecompose", "");
196 argList::validOptions.insert("preReconstruct", "");
197 argList::validOptions.insert("postReconstruct", "");
199 argList::validOptions.insert("global","");
200 argList::validOptions.insert("local","");
202 // default behaviour is purge the case/[time] directory before '-load'
203 // command. '-noPurge' prevents this. Allows for more complicated
204 // load data selections by executing multiSolver several times
205 argList::validOptions.insert("noPurge","");
207 // default behaviour is: if there is only one solverDomain specified, use
208 // setSolverDomain() on it. Same as multiSolver -set solverDomain.
209 // '-noSet' prevents this.
210 argList::validOptions.insert("noSet","");
212 // default behaviour is: if there are storeFields defined, when loading, it
213 // will copy the store fields into every time instance where they are
214 // absent. '-noStore' will prevent this.
215 argList::validOptions.insert("noStore","");
217 # include "setRootCase.H"
236 bool noPurge = false;
238 bool noStore = false;
242 if (args.optionFound("list"))
247 if (args.optionFound("load"))
251 options = args.options()["load"];
253 if (args.optionFound("purge"))
257 options = args.options()["purge"];
259 if (args.optionFound("set"))
263 options = args.options()["set"];
265 if (args.optionFound("preDecompose"))
268 command = preDecompose;
270 if (args.optionFound("postDecompose"))
273 command = postDecompose;
275 if (args.optionFound("preReconstruct"))
278 command = preReconstruct;
280 if (args.optionFound("postReconstruct"))
283 command = postReconstruct;
285 if (args.optionFound("global"))
289 if (args.optionFound("local"))
293 if (args.optionFound("noPurge"))
297 if (args.optionFound("noSet"))
301 if (args.optionFound("noStore"))
309 FatalErrorIn("multiSolver::main")
310 << "multiSolver - nothing to do. Use 'multiSolver -help' for assistance."
311 << abort(FatalError);
313 else if (nCommands > 1)
315 FatalErrorIn("multiSolver::main")
316 << "More than one command found. Use only one of:"
320 << "\n\t-preDecompose"
321 << "\n\t-postDecompose"
322 << "\n\t-preReconstruct"
323 << "\n\t-postReconstruct\n"
324 << abort(FatalError);
328 FatalErrorIn("multiSolver::main")
329 << "Options global and local both specified. Use only one or "
331 << abort(FatalError);
333 if ((command != load) && (noPurge || noSet || noStore))
335 FatalErrorIn("multiSolver::main")
336 << "'noPurge', 'noSet' and 'noStore' can only be used with the "
337 << "'-load' command."
338 << abort(FatalError);
343 Foam::multiSolver::multiControlDictName,
348 const IOdictionary& mcd(multiRun.multiControlDict());
349 wordList solverDomains(0);
350 labelList superLoops(0);
354 && (command != preDecompose)
355 && (command != postDecompose)
356 && (command != preReconstruct)
357 && (command != postReconstruct)
360 parseOptions(&solverDomains, &superLoops, options);
363 // Special words - all, root
364 if (solverDomains.size() == 1)
366 if (solverDomains[0] == "all")
370 else if (solverDomains[0] == "root")
376 // More error checking
377 if (root && ((command == load) || (command == set)))
379 FatalErrorIn("multiSolver::main")
380 << "'root' is not a valid option with '-load' or '-set'"
381 << abort(FatalError);
383 if (all && (command == set))
385 FatalErrorIn("multiSolver::main")
386 << "'all' is not a valid option with '-set'"
387 << abort(FatalError);
389 if ((command == set) && ((solverDomains.size() > 1) || superLoops.size()))
391 FatalErrorIn("multiSolver::main")
392 << "'-set' can only have a single solverDomain name as an option."
393 << abort(FatalError);
395 if (all && superLoops.size())
397 FatalErrorIn("multiSolver::main")
398 << "'all' cannot be followed by superLoop numbers. To specify "
399 << "a superLoop range for all solverDomains, omit the solverDomain"
400 << " name entirely. e.g. multiSolver -load '0:4 6'"
401 << abort(FatalError);
403 if (root && superLoops.size())
405 FatalErrorIn("multiSolver::main")
406 << "'root' cannot be followed by superLoop numbers. 'root' refers"
407 << " to case/[time] directories. There are no superLoops here."
408 << abort(FatalError);
411 // Check for correct solverDomain names
417 && (command != preDecompose)
418 && (command != postDecompose)
419 && (command != preReconstruct)
420 && (command != postReconstruct)
423 forAll(solverDomains, i)
425 if (solverDomains[i] == "default")
427 // default not permitted
428 FatalErrorIn("multiSolver::main")
429 << "'default' is not a permitted solverDomain name."
430 << abort(FatalError);
432 if (!mcd.subDict("solverDomains").found(solverDomains[i]))
434 // Incorrect solver domain name
435 FatalErrorIn("multiSolver::main")
436 << "solverDomainName " << solverDomains[i] << "is not "
438 << abort(FatalError);
443 // Load specified timeClusterLists
444 timeClusterList tclSource(0);
449 tclSource = multiRun.readAllTimes();
452 if (tclSource[i].superLoop() == -1)
454 tclSource[i].times().clear();
457 tclSource.purgeEmpties();
464 && (command != preDecompose)
465 && (command != postDecompose)
466 && (command != preReconstruct)
467 && (command != postReconstruct)
470 // no superLoops specified - read entire solverDomains
471 forAll (solverDomains, sd)
475 multiRun.readSolverDomainTimes(solverDomains[sd])
484 && (command != preDecompose)
485 && (command != postDecompose)
486 && (command != preReconstruct)
487 && (command != postReconstruct)
490 // read individual superLoops
491 if (!solverDomains.size())
493 solverDomains = mcd.subDict("solverDomains").toc();
495 forAll(superLoops, sl)
497 forAll(solverDomains, sd)
499 if (solverDomains[sd] == "default") continue;
502 multiRun.readSuperLoopTimes
512 if (tclSource.size())
514 if (!tclSource.purgeEmpties())
516 FatalErrorIn("multiSolver::main")
517 << "No data found with specified parameters."
518 << abort(FatalError);
526 Info << "Listing available data:\n" << endl;
527 Info << "superLoops by solverDomain:" << endl;
528 solverDomains = mcd.subDict("solverDomains").toc();
531 multiRun.multiDictRegistry().path()/"multiSolver"
534 forAll(solverDomains, i)
536 if (solverDomains[i] == "default") continue;
537 Info << solverDomains[i] << ":" << endl;
538 Info << multiRun.findSuperLoops(listPath/solverDomains[i])
546 // Default behaviour - use local time unless overlapping, then use
547 // global time; if global overlaps, fail. -local and -global force
549 bool localOverlap(!multiRun.nonOverlapping(tclSource, false));
550 bool globalOverlap(!multiRun.nonOverlapping(tclSource, true));
551 if (local && localOverlap)
553 FatalErrorIn("multiSolver::main")
554 << "'-local' option used for data with overlapping local "
555 << "values. Try using a single solverDomain / superLoop, "
556 << "or leave '-local' off."
557 << abort(FatalError);
561 FatalErrorIn("multiSolver::main")
562 << "globalTime values are overlapping. This should not "
563 << "happen. Ensure you have not specified the same "
564 << "solverDomain and/or superLoop more than once. If "
565 << "that fails, try using 'multiSolver -purge all' and "
566 << "rerunning the simulation. If the problem persists, "
568 << abort(FatalError);
573 Info << "Purging existing time directories in case root"
575 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
578 Info << "Loading data from multiSolver directories to case root"
582 !multiRun.loadTimeClusterList
585 global || localOverlap,
590 FatalErrorIn("multiRun::main")
591 << "loadTimeClusterList failed. timeClusterList contents: "
593 << abort(FatalError);
600 Info << "Purging time directories from case root" << endl;
601 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
605 Info << "Purging time directories from multiSolver directories"
609 // do not purge 'initial' directory, even if specified
610 if (tclSource[i].superLoop() < 0) continue;
613 multiRun.findInstancePath(tclSource[i], 0).path()
624 Info << "Performing preDecompose" << endl;
625 multiRun.preCondition();
630 Info << "Performing postDecompose" << endl;
632 fileNameList dirEntries
636 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
640 forAll(dirEntries, de)
642 if (dirEntries[de](9) == "processor")
644 Info << "Reading " << dirEntries[de] << endl;
646 multiRun.postCondition
651 // Copy system to processorN
654 multiRun.multiDictRegistry().path()
655 /multiRun.multiDictRegistry().system(),
656 multiRun.multiDictRegistry().path()/dirEntries[de]
659 // Copy constant/files to processorN/constant
660 fileNameList constantContents
664 multiRun.multiDictRegistry().path()
665 /multiRun.multiDictRegistry().constant(),
669 forAll(constantContents, cc)
673 multiRun.multiDictRegistry().path()
674 /multiRun.multiDictRegistry().constant()
675 /constantContents[cc],
676 multiRun.multiDictRegistry().path()/dirEntries[de]
677 /multiRun.multiDictRegistry().constant()
681 // Copy constant/directories to processorN/constant
682 constantContents = readDir
684 multiRun.multiDictRegistry().path()
685 /multiRun.multiDictRegistry().constant(),
688 forAll(constantContents, cc)
690 // Ingore mesh directory
691 if (constantContents[cc] == "polyMesh")
697 multiRun.multiDictRegistry().path()
698 /multiRun.multiDictRegistry().constant()
699 /constantContents[cc],
700 multiRun.multiDictRegistry().path()/dirEntries[de]
701 /multiRun.multiDictRegistry().constant()
706 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
711 Info << "Performing preReconstruct" << endl;
712 fileNameList dirEntries
716 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
720 forAll(dirEntries, de)
722 if (dirEntries[de](9) == "processor")
724 Info << "Reading " << dirEntries[de] << endl;
725 multiRun.preCondition
729 // Fix missing 0.00000e+00 directory if it exists
732 multiRun.multiDictRegistry().path()/dirEntries[de]/"0"
738 case postReconstruct:
740 Info << "Performing postReconstruct" << endl;
742 Info << "Reading preconditioned time directories" << endl;
743 multiRun.postCondition();
745 Info << "Purging preconditioned time directories"
748 // Clean up extra time directories
749 fileNameList dirEntries
753 multiRun.multiDictRegistry().path(), fileName::DIRECTORY
757 forAll(dirEntries, de)
759 if (dirEntries[de](9) == "processor")
761 multiRun.purgeTimeDirs
763 multiRun.multiDictRegistry().path()/dirEntries[de]
771 // Execute set command - either from an explicit '-set' or from a '-load'
772 // with only one solverDomain as an option
779 && (solverDomains.size() == 1)
784 Info << "Changing to " << solverDomains[0] << " settings." << endl;
785 multiRun.setSolverDomainPostProcessing(solverDomains[0]);
788 Info << "\nCommand completed successfully.\n" << endl;
792 // ************************************************************************* //