Remove trailing whitespace systematically
[foam-extend-3.2.git] / applications / utilities / postProcessing / multiSolver / multiSolver.C
blob651be8d0368c0d1288ca0f9784eb1eb6998f7092
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | foam-extend: Open Source CFD
4    \\    /   O peration     |
5     \\  /    A nd           | For copyright notice see file Copyright
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
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/>.
24 Application
25     multiSolver
27 Description
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.
33 Author
34     David L. F. Gaden
36 \*---------------------------------------------------------------------------*/
38 #include "fvCFD.H"
39 #include "multiSolver.H"
41 void parseOptions
43     wordList * ptrSolverDomains,
44     labelList * ptrSuperLoops,
45     string options
48     IStringStream optionsStream(options);
49     label nSolverDomains(0);
50     label nSuperLoops(0);
52     // Get solverDomainNames, if any
53     while (not optionsStream.eof())
54     {
55         token nextOption(optionsStream);
57         // Bug workaround
58         if (nextOption.type() == token::ERROR)
59         {
60             break;
61         }
63         if (nextOption.isLabel())
64         {
65             ptrSuperLoops->setSize(++nSuperLoops);
66             ptrSuperLoops->operator[](nSuperLoops - 1)
67                 = nextOption.labelToken();
68             break;
69         }
70         if (nextOption.isWord())
71         {
72             ptrSolverDomains->setSize(++nSolverDomains);
73             ptrSolverDomains->operator[](nSolverDomains - 1)
74                 = nextOption.wordToken();
75         }
76         else
77         {
78             // not word, not label, fail
79             FatalErrorIn("multiSolver::parseOptions")
80                 << "Expecting word or label.  Neither found at position "
81                 << nSolverDomains - 1 << " in " << options
82                 << abort(FatalError);
83         }
84     }
86     // Get superLoopList
87     while (not optionsStream.eof())
88     {
89         token nextOption(optionsStream);
91         // Bug workaround
92         if (nextOption.type() == token::ERROR)
93         {
94             break;
95         }
97         if (nextOption.isLabel())
98         {
99             ptrSuperLoops->setSize(++nSuperLoops);
100             ptrSuperLoops->operator[](nSuperLoops - 1)
101                 = nextOption.labelToken();
102         }
103         else if (nSuperLoops > 0)
104         {
105             // might be a range -> label : label
107             if (nextOption.isPunctuation())
108             {
109                 token::punctuationToken p(nextOption.pToken());
110                 if (p == token::COLON)
111                 {
112                     token nextNextOption(optionsStream);
113                     if (nextNextOption.isLabel())
114                     {
115                         label toValue(nextNextOption.labelToken());
116                         label fromValue
117                         (
118                             ptrSuperLoops->operator[](nSuperLoops - 1)
119                         );
121                         if (toValue > fromValue)
122                         {
123                             // correct range format
124                             for (label i = fromValue + 1; i <= toValue; i++)
125                             {
126                                 ptrSuperLoops->setSize(++nSuperLoops);
127                                 ptrSuperLoops->operator[](nSuperLoops - 1) = i;
128                             }
129                         }
130                         else
131                         {
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
137                                 << " : " << toValue
138                                 << abort(FatalError);
139                         }
140                     }
141                     else
142                     {
143                         // nextNext not label
144                         FatalErrorIn("multiSolver::parseOptions")
145                             << "Incorrect syntax.  Expecting label after ':' "
146                             << "in " << options
147                             << abort(FatalError);
148                     }
149                 }
150                 else
151                 {
152                     // non : punctuation
153                     FatalErrorIn("multiSolver::parseOptions")
154                         << "Incorrect syntax.  Expecting label, word, or ':' "
155                         << "in " << options
156                         << abort(FatalError);
157                 }
158             }
159             else
160             {
161                 // not punctuation
162                 FatalErrorIn("multiSolver::parseOptions")
163                     << "Incorrect syntax.  Expecting label, word, or ':' "
164                     << "in " << options
165                     << abort(FatalError);
166             }
167         }
168         else
169         {
170             // not label, not word
171             FatalErrorIn("multiSolver::parseOptions")
172                 << "Incorrect syntax.  Expecting label, word, or ':' "
173                 << "in " << options
174                 << abort(FatalError);
175         }
176     }
180 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
182 int main(int argc, char *argv[])
184     argList::validOptions.insert("list","");
185     argList::validOptions.insert
186     (
187         "load","<[solverDomainName] [superLoopNumber(s)]>"
188     );
189     argList::validOptions.insert
190     (
191         "purge","<[solverDomainName] [superLoopNumber(s)]>"
192     );
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"
219     enum commandType
220     {
221         list,
222         load,
223         purge,
224         set,
225         preDecompose,
226         postDecompose,
227         preReconstruct,
228         postReconstruct
229     };
230     commandType command;
231     string options;
232     bool global = false;
233     bool local = false;
234     bool all = false;
235     bool root = false;
236     bool noPurge = false;
237     bool noSet = false;
238     bool noStore = false;
239     label nCommands(0);
241     // Read arguments
242     if (args.optionFound("list"))
243     {
244         nCommands++;
245         command = list;
246     }
247     if (args.optionFound("load"))
248     {
249         nCommands++;
250         command = load;
251         options = args.options()["load"];
252     }
253     if (args.optionFound("purge"))
254     {
255         nCommands++;
256         command = purge;
257         options = args.options()["purge"];
258     }
259     if (args.optionFound("set"))
260     {
261         nCommands++;
262         command = set;
263         options = args.options()["set"];
264     }
265     if (args.optionFound("preDecompose"))
266     {
267         nCommands++;
268         command = preDecompose;
269     }
270     if (args.optionFound("postDecompose"))
271     {
272         nCommands++;
273         command = postDecompose;
274     }
275     if (args.optionFound("preReconstruct"))
276     {
277         nCommands++;
278         command = preReconstruct;
279     }
280     if (args.optionFound("postReconstruct"))
281     {
282         nCommands++;
283         command = postReconstruct;
284     }
285     if (args.optionFound("global"))
286     {
287         global = true;
288     }
289     if (args.optionFound("local"))
290     {
291         local = true;
292     }
293     if (args.optionFound("noPurge"))
294     {
295         noPurge = true;
296     }
297     if (args.optionFound("noSet"))
298     {
299         noSet = true;
300     }
301     if (args.optionFound("noStore"))
302     {
303         noStore = true;
304     }
306     // Error checking
307     if (nCommands == 0)
308     {
309         FatalErrorIn("multiSolver::main")
310             << "multiSolver - nothing to do.  Use 'multiSolver -help' for assistance."
311             << abort(FatalError);
312     }
313     else if (nCommands > 1)
314     {
315         FatalErrorIn("multiSolver::main")
316             << "More than one command found.  Use only one of:"
317             << "\n\t-list"
318             << "\n\t-purge"
319             << "\n\t-set"
320             << "\n\t-preDecompose"
321             << "\n\t-postDecompose"
322             << "\n\t-preReconstruct"
323             << "\n\t-postReconstruct\n"
324             << abort(FatalError);
325     }
326     if (global && local)
327     {
328         FatalErrorIn("multiSolver::main")
329             << "Options global and local both specified.  Use only one or "
330             << "none."
331             << abort(FatalError);
332     }
333     if ((command != load) && (noPurge || noSet || noStore))
334     {
335         FatalErrorIn("multiSolver::main")
336             << "'noPurge', 'noSet' and 'noStore' can only be used with the "
337             << "'-load' command."
338             << abort(FatalError);
339     }
341     multiSolver multiRun
342     (
343         Foam::multiSolver::multiControlDictName,
344         args.rootPath(),
345         args.caseName()
346     );
348     const IOdictionary& mcd(multiRun.multiControlDict());
349     wordList solverDomains(0);
350     labelList superLoops(0);
351     if
352     (
353         (command != list)
354      && (command != preDecompose)
355      && (command != postDecompose)
356      && (command != preReconstruct)
357      && (command != postReconstruct)
358     )
359     {
360         parseOptions(&solverDomains, &superLoops, options);
361     }
363     // Special words - all, root
364     if (solverDomains.size() == 1)
365     {
366         if (solverDomains[0] == "all")
367         {
368             all = true;
369         }
370         else if (solverDomains[0] == "root")
371         {
372             root = true;
373         }
374     }
376     // More error checking
377     if (root && ((command == load) || (command == set)))
378     {
379         FatalErrorIn("multiSolver::main")
380             << "'root' is not a valid option with '-load' or '-set'"
381             << abort(FatalError);
382     }
383     if (all && (command == set))
384     {
385         FatalErrorIn("multiSolver::main")
386             << "'all' is not a valid option with '-set'"
387             << abort(FatalError);
388     }
389     if ((command == set) && ((solverDomains.size() > 1) || superLoops.size()))
390     {
391         FatalErrorIn("multiSolver::main")
392             << "'-set' can only have a single solverDomain name as an option."
393             << abort(FatalError);
394     }
395     if (all && superLoops.size())
396     {
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);
402     }
403     if (root && superLoops.size())
404     {
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);
409     }
411     // Check for correct solverDomain names
412     if
413     (
414         !all
415      && !root
416      && (command != list)
417      && (command != preDecompose)
418      && (command != postDecompose)
419      && (command != preReconstruct)
420      && (command != postReconstruct)
421     )
422     {
423         forAll(solverDomains, i)
424         {
425             if (solverDomains[i] == "default")
426             {
427                 // default not permitted
428                 FatalErrorIn("multiSolver::main")
429                     << "'default' is not a permitted solverDomain name."
430                     << abort(FatalError);
431             }
432             if (!mcd.subDict("solverDomains").found(solverDomains[i]))
433             {
434                 // Incorrect solver domain name
435                 FatalErrorIn("multiSolver::main")
436                     << "solverDomainName " << solverDomains[i] << "is not "
437                     << "found."
438                     << abort(FatalError);
439             }
440         }
441     }
443     // Load specified timeClusterLists
444     timeClusterList tclSource(0);
446     if (all)
447     {
448         // read all
449         tclSource = multiRun.readAllTimes();
450         forAll(tclSource, i)
451         {
452             if (tclSource[i].superLoop() == -1)
453             {
454                 tclSource[i].times().clear();
455             }
456         }
457         tclSource.purgeEmpties();
458     }
459     else if
460     (
461         !superLoops.size()
462      && (command != set)
463      && (command != list)
464      && (command != preDecompose)
465      && (command != postDecompose)
466      && (command != preReconstruct)
467      && (command != postReconstruct)
468     )
469     {
470         // no superLoops specified - read entire solverDomains
471         forAll (solverDomains, sd)
472         {
473             tclSource.append
474             (
475                 multiRun.readSolverDomainTimes(solverDomains[sd])
476             );
477         }
478     }
479     else if
480     (
481         !root
482      && (command != set)
483      && (command != list)
484      && (command != preDecompose)
485      && (command != postDecompose)
486      && (command != preReconstruct)
487      && (command != postReconstruct)
488     )
489     {
490         // read individual superLoops
491         if (!solverDomains.size())
492         {
493             solverDomains = mcd.subDict("solverDomains").toc();
494         }
495         forAll(superLoops, sl)
496         {
497             forAll(solverDomains, sd)
498             {
499                 if (solverDomains[sd] == "default") continue;
500                 tclSource.append
501                 (
502                     multiRun.readSuperLoopTimes
503                     (
504                         solverDomains[sd],
505                         superLoops[sl]
506                     )
507                 );
508             }
509         }
510     }
512     if (tclSource.size())
513     {
514         if (!tclSource.purgeEmpties())
515         {
516             FatalErrorIn("multiSolver::main")
517                 << "No data found with specified parameters."
518                 << abort(FatalError);
519         }
520     }
522     switch (command)
523     {
524         case list:
525         {
526             Info << "Listing available data:\n" << endl;
527             Info << "superLoops by solverDomain:" << endl;
528             solverDomains = mcd.subDict("solverDomains").toc();
529             fileName listPath
530             (
531                 multiRun.multiDictRegistry().path()/"multiSolver"
532             );
534             forAll(solverDomains, i)
535             {
536                 if (solverDomains[i] == "default") continue;
537                 Info << solverDomains[i] << ":" << endl;
538                 Info << multiRun.findSuperLoops(listPath/solverDomains[i])
539                     << endl;
540             }
541             Info << endl;
542             break;
543         }
544         case load:
545         {
546             // Default behaviour - use local time unless overlapping, then use
547             // global time; if global overlaps, fail.  -local and -global force
548             // the behaviour
549             bool localOverlap(!multiRun.nonOverlapping(tclSource, false));
550             bool globalOverlap(!multiRun.nonOverlapping(tclSource, true));
551             if (local && localOverlap)
552             {
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);
558             }
559             if (globalOverlap)
560             {
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, "
567                     << "it is a bug."
568                     << abort(FatalError);
569             }
571             if (!noPurge)
572             {
573                 Info << "Purging existing time directories in case root"
574                     << endl;
575                 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
576             }
578             Info << "Loading data from multiSolver directories to case root"
579                 << endl;
580             if
581             (
582                 !multiRun.loadTimeClusterList
583                 (
584                     tclSource,
585                     global || localOverlap,
586                     !noStore
587                 )
588             )
589             {
590                 FatalErrorIn("multiRun::main")
591                     << "loadTimeClusterList failed.  timeClusterList contents: "
592                     << tclSource
593                     << abort(FatalError);
594             }
595             break;
596         }
597         case purge:
598             if (root)
599             {
600                 Info << "Purging time directories from case root" << endl;
601                 multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
602             }
603             else
604             {
605                 Info << "Purging time directories from multiSolver directories"
606                     << endl;
607                 forAll(tclSource, i)
608                 {
609                     // do not purge 'initial' directory, even if specified
610                     if (tclSource[i].superLoop() < 0) continue;
611                     fileName purgePath
612                     (
613                         multiRun.findInstancePath(tclSource[i], 0).path()
614                     );
615                     rmDir(purgePath);
616                 }
617             }
618             break;
619         case set:
620             // do nothing here
621             break;
622         case preDecompose:
623         {
624             Info << "Performing preDecompose" << endl;
625             multiRun.preCondition();
626             break;
627         }
628         case postDecompose:
629         {
630             Info << "Performing postDecompose" << endl;
632             fileNameList dirEntries
633             (
634                 readDir
635                 (
636                     multiRun.multiDictRegistry().path(), fileName::DIRECTORY
637                 )
638             );
640             forAll(dirEntries, de)
641             {
642                 if (dirEntries[de](9) == "processor")
643                 {
644                     Info << "Reading " << dirEntries[de] << endl;
646                     multiRun.postCondition
647                     (
648                         dirEntries[de]
649                     );
651                     // Copy system to processorN
652                     cp
653                     (
654                         multiRun.multiDictRegistry().path()
655                             /multiRun.multiDictRegistry().system(),
656                         multiRun.multiDictRegistry().path()/dirEntries[de]
657                     );
659                     // Copy constant/files to processorN/constant
660                     fileNameList constantContents
661                     (
662                         readDir
663                         (
664                             multiRun.multiDictRegistry().path()
665                                 /multiRun.multiDictRegistry().constant(),
666                             fileName::FILE
667                         )
668                     );
669                     forAll(constantContents, cc)
670                     {
671                         cp
672                         (
673                             multiRun.multiDictRegistry().path()
674                                 /multiRun.multiDictRegistry().constant()
675                                 /constantContents[cc],
676                             multiRun.multiDictRegistry().path()/dirEntries[de]
677                                 /multiRun.multiDictRegistry().constant()
678                         );
679                     }
681                     // Copy constant/directories to processorN/constant
682                     constantContents = readDir
683                     (
684                         multiRun.multiDictRegistry().path()
685                             /multiRun.multiDictRegistry().constant(),
686                         fileName::DIRECTORY
687                     );
688                     forAll(constantContents, cc)
689                     {
690                         // Ingore mesh directory
691                         if (constantContents[cc] == "polyMesh")
692                         {
693                             continue;
694                         }
695                         cp
696                         (
697                             multiRun.multiDictRegistry().path()
698                                 /multiRun.multiDictRegistry().constant()
699                                 /constantContents[cc],
700                             multiRun.multiDictRegistry().path()/dirEntries[de]
701                                 /multiRun.multiDictRegistry().constant()
702                         );
703                     }
704                 }
705             }
706             multiRun.purgeTimeDirs(multiRun.multiDictRegistry().path());
707             break;
708         }
709         case preReconstruct:
710         {
711             Info << "Performing preReconstruct" << endl;
712             fileNameList dirEntries
713             (
714                 readDir
715                 (
716                     multiRun.multiDictRegistry().path(), fileName::DIRECTORY
717                 )
718             );
720             forAll(dirEntries, de)
721             {
722                 if (dirEntries[de](9) == "processor")
723                 {
724                     Info << "Reading " << dirEntries[de] << endl;
725                     multiRun.preCondition
726                     (
727                         dirEntries[de]
728                     );
729                     // Fix missing 0.00000e+00 directory if it exists
730                     mkDir
731                     (
732                         multiRun.multiDictRegistry().path()/dirEntries[de]/"0"
733                     );
734                 }
735             }
736             break;
737         }
738         case postReconstruct:
739         {
740             Info << "Performing postReconstruct" << endl;
742             Info << "Reading preconditioned time directories" << endl;
743             multiRun.postCondition();
745             Info << "Purging preconditioned time directories"
746                 << endl;
748             // Clean up extra time directories
749             fileNameList dirEntries
750             (
751                 readDir
752                 (
753                     multiRun.multiDictRegistry().path(), fileName::DIRECTORY
754                 )
755             );
757             forAll(dirEntries, de)
758             {
759                 if (dirEntries[de](9) == "processor")
760                 {
761                     multiRun.purgeTimeDirs
762                     (
763                         multiRun.multiDictRegistry().path()/dirEntries[de]
764                     );
765                 }
766             }
767             break;
768         }
769     }
771     // Execute set command - either from an explicit '-set' or from a '-load'
772     // with only one solverDomain as an option
774     if
775     (
776         (command == set)
777      || (
778             (command == load)
779          && (solverDomains.size() == 1)
780          && (!all)
781         )
782     )
783     {
784         Info << "Changing to " << solverDomains[0] << " settings." << endl;
785         multiRun.setSolverDomainPostProcessing(solverDomains[0]);
786     }
788     Info << "\nCommand completed successfully.\n" << endl;
789     return(0);
792 // ************************************************************************* //