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