fileOperation.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2017-2018 OpenFOAM Foundation
9 Copyright (C) 2019-2021 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "fileOperation.H"
31#include "regIOobject.H"
32#include "argList.H"
33#include "HashSet.H"
34#include "objectRegistry.H"
35#include "decomposedBlockData.H"
36#include "polyMesh.H"
37#include "registerSwitch.H"
38#include "Time.H"
39#include "ITstream.H"
40#include <cerrno>
41#include <cinttypes>
42
43/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
44
45namespace Foam
46{
49
51 (
52 debug::optimisationSwitches().getOrAdd<word>
53 (
54 "fileHandler",
55 //Foam::fileOperations::uncollatedFileOperation::typeName,
56 "uncollated",
58 )
59 );
60}
61
64({
65 { fileOperation::NOTFOUND, "notFound" },
66 { fileOperation::ABSOLUTE, "absolute" },
67 { fileOperation::OBJECT, "objectPath" },
68 { fileOperation::WRITEOBJECT, "writeObject" },
69 { fileOperation::PROCUNCOLLATED, "uncollatedProc" },
70 { fileOperation::PROCBASEOBJECT, "globalProc" },
71 { fileOperation::PROCOBJECT, "localProc" },
72 { fileOperation::PARENTOBJECT, "parentObjectPath" },
73 { fileOperation::FINDINSTANCE, "findInstance" },
74 { fileOperation::PROCUNCOLLATEDINSTANCE, "uncollatedProcInstance" },
75 { fileOperation::PROCBASEINSTANCE, "globalProcInstance" },
76 { fileOperation::PROCINSTANCE, "localProcInstance" }
77});
78
79
81
83
84
85// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
86
87namespace
88{
89
90// Need to parse the numbers
91// from "processors(\d+)" and
92// from "processors(\d+)_(\d+)-(\d+)"
93//
94// Receive the string matching "^(\d+)(?:_(\d+)-(\d+))?/?$"
95//
96// \1 = numProcs
97// \2 = firstProc
98// \3 = lastProc
99//
100// Return true on success and set parameters numProcs and group (size,start)
101//
102// Use low-level C-string to integer parsing to drive the sequence.
103//
104// For simplicity, also skip INT_MAX checks everywhere but check for
105// - (errno) for success
106// - (nptr == endptr) for leading junk
107// - (*endptr != endChar) for trailing junk
108// - skip INT_MAX checks as being too pessimistic
109
110static bool parseProcsNumRange
111(
112 const std::string str,
113 int& numProcs,
115)
116{
117 const char * nptr = str.c_str();
118 char *endptr = nullptr;
119
120 // 1. numProcs
121 errno = 0;
122 intmax_t parsed = std::strtoimax(nptr, &endptr, 10);
123 if (errno || nptr == endptr) return false; // bad parse
124
125 const int nProcs = int(parsed);
126
127 // End of string? Then no range and we are done.
128 if (*endptr == '\0')
129 {
130 numProcs = nProcs;
131 return true;
132 }
133
134 // Parse point at start of range ('_' character)?
135 if (*endptr != '_') return false;
136 nptr = ++endptr;
137
138
139 // 2. firstProc
140 errno = 0;
141 parsed = std::strtoimax(nptr, &endptr, 10);
142 if (errno || nptr == endptr) return false; // bad parse
143
144 const int firstProc = int(parsed);
145
146 // Parse point at range separator ('-' character)?
147 if (*endptr != '-') return false;
148 nptr = ++endptr;
149
150
151 // 3. lastProc
152 errno = 0;
153 parsed = std::strtoimax(nptr, &endptr, 10);
154 if (errno || nptr == endptr) return false; // bad parse
155
156 const int lastProc = int(parsed);
157
158
159 if
160 (
161 // Parse point at end of string
162 (*endptr == '\0')
163
164 // Input plausibility
165 // Accept nProcs == 0 in case that becomes useful in the future
166 && (nProcs >= 0 && firstProc >= 0 && firstProc <= lastProc)
167 )
168 {
169 numProcs = nProcs;
170
171 // Convert first/last to start/size
172 group.reset(firstProc, lastProc-firstProc+1);
173
174 return true;
175 }
176
177 return false;
178}
179
180} // End anonymous namespace
181
182
183#if 0
184
185// Sorting of processor directories
186#include "stringOpsSort.H"
187namespace
188{
189
190// Sort processor directory names (natural order)
191// - not strictly necessary
192void sortProcessorDirs(Foam::UList<Foam::fileOperation::dirIndex>& dirs)
193{
194 if (dirs.size() > 1)
195 {
196 std::stable_sort
197 (
198 dirs.begin(),
199 dirs.end(),
200 []
201 (
204 ) -> bool
205 {
206 return
207 Foam::stringOps::natural_sort::compare
208 (
209 a.first(),
210 b.first()
211 ) < 0;
212 }
213 );
214 }
215}
216
217} // End anonymous namespace
218#endif
219
220// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
221
223{
224 labelList ranks;
225
226 ITstream is(Foam::getEnv("FOAM_IORANKS"));
227 if (!is.empty())
228 {
229 is >> ranks;
230 }
231
232 return ranks;
233}
234
235
238(
239 const fileNameList& dirEntries,
240 const word& constantName
241)
242{
243 // Check for "constant"
244 bool haveConstant = false;
245
246 if (!constantName.empty())
247 {
248 for (const fileName& dirName : dirEntries)
249 {
250 if (dirName == constantName)
251 {
252 haveConstant = true;
253 break;
254 }
255 }
256 }
257
258 instantList times(dirEntries.size() + 1);
259 label nTimes = 0;
260
261 if (haveConstant)
262 {
263 times[nTimes].value() = 0;
264 times[nTimes].name() = constantName;
265 ++nTimes;
266 }
267
268 // Parse directory entries for scalar values
269 for (const fileName& dirName : dirEntries)
270 {
271 if (readScalar(dirName, times[nTimes].value()))
272 {
273 times[nTimes].name() = dirName;
274 ++nTimes;
275 }
276 }
277
278 times.resize(nTimes);
279
280 if (haveConstant)
281 {
282 if (nTimes > 2)
283 {
284 std::sort(&times[1], times.end(), instant::less());
285 }
286 }
287 else if (nTimes > 1)
288 {
289 std::sort(times.begin(), times.end(), instant::less());
290 }
291
292 return times;
293}
294
295
296// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
297
299{
300 if (!monitorPtr_)
301 {
302 monitorPtr_.reset
303 (
304 new fileMonitor
305 (
308 )
309 );
310 }
311 return *monitorPtr_;
312}
313
314
316(
317 const instantList& extraTimes,
318 const word& constantName,
319 instantList& times
320)
321{
322 if (extraTimes.size())
323 {
324 const bool haveConstant =
325 (
326 times.size()
327 && times[0].name() == constantName
328 );
329
330 const bool haveExtraConstant =
331 (
332 extraTimes.size()
333 && extraTimes[0].name() == constantName
334 );
335
336 // Combine times
337 instantList combinedTimes(times.size()+extraTimes.size());
338 label sz = 0;
339 label extrai = 0;
340 if (haveExtraConstant)
341 {
342 extrai = 1;
343 if (!haveConstant)
344 {
345 combinedTimes[sz++] = extraTimes[0]; // constant
346 }
347 }
348 forAll(times, i)
349 {
350 combinedTimes[sz++] = times[i];
351 }
352 for (; extrai < extraTimes.size(); extrai++)
353 {
354 combinedTimes[sz++] = extraTimes[extrai];
355 }
356 combinedTimes.setSize(sz);
357 times.transfer(combinedTimes);
358
359 // Sort
360 if (times.size() > 1)
361 {
362 label starti = 0;
363 if (times[0].name() == constantName)
364 {
365 starti = 1;
366 }
367 std::sort(&times[starti], times.end(), instant::less());
368
369 // Filter out duplicates
370 label newi = starti+1;
371 for (label i = newi; i < times.size(); i++)
372 {
373 if (times[i].value() != times[i-1].value())
374 {
375 if (newi != i)
376 {
377 times[newi] = times[i];
378 }
379 newi++;
380 }
381 }
382
383 times.setSize(newi);
384 }
385 }
386}
387
388
390{
391 return (isFile ? Foam::isFile(f) : Foam::isDir(f));
392}
393
394
397(
398 const fileName& fName,
399 const bool syncPar
400) const
401{
402 // If path is local to a processor (e.g. contains 'processor2')
403 // find the corresponding actual processor directory (e.g. 'processors4')
404 // and index (2)
405
406 fileName path, pDir, local;
407 procRangeType group;
408 label numProcs;
409 const label proci =
410 splitProcessorPath(fName, path, pDir, local, group, numProcs);
411
412 if (proci != -1)
413 {
414 const fileName procPath(path/pDir);
415
416 const auto iter = procsDirs_.cfind(procPath);
417
418 if (iter.found())
419 {
420 return iter.val();
421 }
422
423 DynamicList<dirIndex> procDirs;
424 fileNameList dirEntries;
425
426 // Read all directories to see any beginning with processor
427 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
428
429 // Note: use parallel synchronised reading so cache will be same
430 // order on all processors
431
432 const bool readDirMasterOnly
433 (
434 Pstream::parRun() && !distributed()
435 &&
436 (
439 )
440 );
441
442 // The above selection excludes masterUncollated, which uses inotify or
443 // timeStamp but provides its own internals for readDir() anyhow.
444
445 if (readDirMasterOnly)
446 {
447 // Parallel and non-distributed
448 // Read on master only and send to subProcs
449
450 if (Pstream::master(comm_))
451 {
453
455 << "readDir on master: send " << dirEntries.size()
456 << " names to sub-processes" << endl;
457 }
458
459 Pstream::broadcast(dirEntries, comm_);
460 }
461 else
462 {
463 // Serial or distributed roots.
464 // Handle readDir() with virtual method
465
466 if (debug)
467 {
468 Pout<< "readDir without special master/send treatment"
469 << endl;
470 }
471
473 }
474
475 // Extract info from processorN or processorsNN
476 // - highest processor number
477 // - directory+offset containing data for proci
478
479 label nProcs = 0;
480 for (const fileName& dirN : dirEntries)
481 {
482 // Analyse directory name
483 fileName rp, rd, rl;
484 label rNum;
485 const label readProci =
486 splitProcessorPath(dirN, rp, rd, rl, group, rNum);
487
488 nProcs = max(nProcs, readProci+1);
489
490 Tuple2<pathType, int> pathTypeIdx(pathType::NOTFOUND, 0);
491
492 if (proci == readProci)
493 {
494 // Found "processorN"
495 pathTypeIdx.first() = pathType::PROCUNCOLLATED;
496 }
497 else if (rNum != -1)
498 {
499 // "processorsNN" or "processorsNN_start-end"
500 nProcs = max(nProcs, rNum);
501
502 if (group.empty())
503 {
504 // "processorsNN"
505
506 if (proci < rNum)
507 {
508 // And it is also in range.
509 // Eg for "processors4": 3 is ok, 10 is not
510
511 pathTypeIdx.first() = pathType::PROCBASEOBJECT;
512 pathTypeIdx.second() = proci;
513 }
514 }
515 else if (group.found(proci))
516 {
517 // "processorsNN_start-end"
518 // - save the local proc offset
519
520 pathTypeIdx.first() = pathType::PROCOBJECT;
521 pathTypeIdx.second() = (proci - group.start());
522 }
523 }
524
525 if (pathTypeIdx.first() != pathType::NOTFOUND)
526 {
527 procDirs.append(dirIndex(dirN, pathTypeIdx));
528 }
529 }
530
531 // Global check of empty/exists.
532 // 1 : empty directory
533 // 2 : non-empty directory
534 // 3 : mixed empty/non-empty directory (after reduce)
535 // Combines andOp<bool>() and orOp<bool>() in single operation
536
537 unsigned procDirsStatus = (procDirs.empty() ? 1u : 2u);
538
539 if (debug)
540 {
541 Pout<< "fileOperation::lookupProcessorsPath " << procPath
542 << " detected:" << procDirs << endl;
543 }
544
545 if (Pstream::parRun() && (!distributed() || syncPar))
546 {
547 reduce(procDirsStatus, bitOrOp<unsigned>()); // worldComm
548
549 if (procDirsStatus == 3u)
550 {
551 // Mixed empty/exists for procDirs.
552 // Synthesize missing directory name (consistency in cache
553 // existence).
554 // Cannot reliably synthesize RANK-COLLATED, only COLLATED or
555 // UNCOLLATED.
556 //
557 // RANK-COLLATED should have been read from its corresponding
558 // master anyhow
559
560 int flavour(pathType::PROCUNCOLLATED);
561 for (const dirIndex& pDir : procDirs)
562 {
563 flavour = max(flavour, int(pDir.second().first()));
564 }
565
566 reduce(nProcs, maxOp<label>()); // worldComm
567 reduce(flavour, maxOp<int>()); // worldComm
568
569 if (procDirs.empty())
570 {
571 Tuple2<pathType, int> pathTypeIdx(pathType(flavour), 0);
572
573 if
574 (
575 pathTypeIdx.first() == pathType::PROCBASEOBJECT
576 && proci < nProcs
577 )
578 {
579 pathTypeIdx.second() = proci;
580
581 procDirs.append
582 (
584 (
585 processorsBaseDir + Foam::name(nProcs),
586 pathTypeIdx
587 )
588 );
589 }
590 else
591 {
592 // - pathType::PROCUNCOLLATED
593 // - poor fallback for pathType::PROCOBJECT
594 // - out-of-range pathType::PROCBASEOBJECT
595
596 procDirs.append
597 (
599 (
600 "processor" + Foam::name(proci),
601 pathTypeIdx
602 )
603 );
604 }
605
606 if (debug)
607 {
608 Pout<< "fileOperation::lookupProcessorsPath "
609 << procPath
610 << " synthetic:" << procDirs << endl;
611 }
612 }
613 }
614 }
615 else if (!Pstream::parRun())
616 {
617 // Serial: use the number of decompositions (if found)
618 if (nProcs)
619 {
620 const_cast<fileOperation&>(*this).setNProcs(nProcs);
621 }
622 }
623
624 // Sort processor directory names (natural order)
626
627 if (procDirsStatus & 2u)
628 {
629 procsDirs_.insert(procPath, procDirs);
630
631 // Make sure to return a reference
632 return procsDirs_[procPath];
633 }
634 }
635
637}
638
639
642{
643 // Use parallel synchronisation
644 return lookupAndCacheProcessorsPath(fName, true);
645}
646
647
649{
650 // Generate output filename for object
651 fileName objPath(objectPath(io, word::null));
652
653 // Test for either directory or a (valid) file & IOobject
654 bool ok;
655 if (io.name().empty())
656 {
657 ok = isDir(objPath);
658 }
659 else
660 {
661 ok =
662 isFile(objPath)
663 && io.typeHeaderOk<IOList<label>>(false);// object with local scope
664 }
665
666 if (!ok)
667 {
668 // Re-test with searched for objectPath. This is for backwards
669 // compatibility
670 fileName originalPath(filePath(io.objectPath()));
671 if (originalPath != objPath)
672 {
673 // Test for either directory or a (valid) file & IOobject
674 if (io.name().empty())
675 {
676 ok = isDir(originalPath);
677 }
678 else
679 {
680 ok =
681 isFile(originalPath)
682 && io.typeHeaderOk<IOList<label>>(false);
683 }
684 }
685 }
686
687 return ok;
688}
689
690
691// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
692
694(
695 const label comm,
696 const bool distributedRoots
697)
698:
699 comm_(comm),
700 distributed_(distributedRoots)
701{}
702
703
706(
707 const word& handlerType,
708 bool verbose
709)
710{
712 << "Constructing fileHandler" << endl;
713
714 auto* ctorPtr = wordConstructorTable(handlerType);
715
716 if (!ctorPtr)
717 {
719 (
720 "fileHandler",
721 handlerType,
722 *wordConstructorTablePtr_
723 ) << abort(FatalError);
724 }
725
726 return autoPtr<fileOperation>(ctorPtr(verbose));
727}
728
729
730// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
731
732bool Foam::fileOperation::distributed(bool on) const noexcept
733{
734 bool old(distributed_);
735 distributed_ = on;
736 return old;
737}
738
739
741(
742 const IOobject& io,
743 const word& typeName
744) const
745{
746 return io.objectPath();
747}
748
749
751(
752 const regIOobject& io,
753 IOstreamOption streamOpt,
754 const bool valid
755) const
756{
757 if (valid)
758 {
759 const fileName pathName(io.objectPath());
760
761 mkDir(pathName.path());
762
763 autoPtr<OSstream> osPtr(NewOFstream(pathName, streamOpt));
764
765 if (!osPtr)
766 {
767 return false;
768 }
769
770 OSstream& os = *osPtr;
771
772 // Update meta-data for current state
773 const_cast<regIOobject&>(io).updateMetaData();
774
775 // If any of these fail, return (leave error handling to Ostream class)
776
777 const bool ok =
778 (
779 os.good()
780 && io.writeHeader(os)
781 && io.writeData(os)
782 );
783
784 if (ok)
785 {
787 }
788
789 return ok;
790 }
791 return true;
792}
793
794
796{
797 if (debug)
798 {
799 Pout<< "fileOperation::filePath :" << " fName:" << fName << endl;
800 }
801
802 fileName path, pDir, local;
803 procRangeType group;
804 label numProcs;
805 label proci =
806 splitProcessorPath(fName, path, pDir, local, group, numProcs);
807
808 if (numProcs != -1)
809 {
810 WarningInFunction << "Filename is already adapted:" << fName << endl;
811 }
812
813 // Give preference to processors variant
814 if (proci != -1)
815 {
816 // Get all processor directories
817 refPtr<dirIndexList> procDirs(lookupProcessorsPath(fName));
818 for (const dirIndex& dirIdx : procDirs())
819 {
820 const fileName& procDir = dirIdx.first();
821
822 fileName collatedName(path/procDir/local);
823 if (exists(collatedName))
824 {
825 if (debug)
826 {
827 Pout<< "fileOperation::filePath : " << collatedName << endl;
828 }
829 return collatedName;
830 }
831 }
832 }
833
834 if (exists(fName))
835 {
836 if (debug)
837 {
838 Pout<< "fileOperation::filePath : " << fName << endl;
839 }
840 return fName;
841 }
842
843 if (debug)
844 {
845 Pout<< "fileOperation::filePath : Not found" << endl;
846 }
847 return fileName::null;
848}
849
850
851Foam::label Foam::fileOperation::addWatch(const fileName& fName) const
852{
853 return monitor().addWatch(fName);
854}
855
856
857bool Foam::fileOperation::removeWatch(const label watchIndex) const
858{
859 return monitor().removeWatch(watchIndex);
860}
861
862
864(
865 const labelList& watchIndices,
866 const fileName& fName
867) const
868{
869 forAll(watchIndices, i)
870 {
871 if (getFile(watchIndices[i]) == fName)
872 {
873 return i;
874 }
875 }
876 return -1;
877}
878
879
881(
882 regIOobject& rio,
883 const fileNameList& files
884) const
885{
886 const labelList& watchIndices = rio.watchIndices();
887
888 DynamicList<label> newWatchIndices;
889 labelHashSet removedWatches(watchIndices);
890
891 for (const fileName& f : files)
892 {
893 const label index = findWatch(watchIndices, f);
894
895 if (index == -1)
896 {
897 newWatchIndices.append(addWatch(f));
898 }
899 else
900 {
901 // Existing watch
902 newWatchIndices.append(watchIndices[index]);
903 removedWatches.erase(index);
904 }
905 }
906
907 // Remove any unused watches
908 for (const label index : removedWatches)
909 {
910 removeWatch(watchIndices[index]);
911 }
912
913 rio.watchIndices() = newWatchIndices;
914}
915
916
917Foam::fileName Foam::fileOperation::getFile(const label watchIndex) const
918{
919 return monitor().getFile(watchIndex);
920}
921
922
924(
925 const bool masterOnly,
926 const bool syncPar
927) const
928{
929 monitor().updateStates(masterOnly, Pstream::parRun());
930}
931
932
934(
935 const label watchFd
936) const
937{
938 return monitor().getState(watchFd);
939}
940
941
942void Foam::fileOperation::setUnmodified(const label watchFd) const
943{
944 monitor().setUnmodified(watchFd);
945}
946
947
949(
950 const fileName& directory,
951 const word& constantName
952) const
953{
954 if (debug)
955 {
956 Pout<< "fileOperation::findTimes : Finding times in directory "
957 << directory << endl;
958 }
959
960 // Note: do NOT use master-only reading here (as per lookupProcessorsPath)
961 // since this routine is called on an individual processorN directory
962
963 // Read directory entries into a list
964 fileNameList dirEntries(Foam::readDir(directory, fileName::DIRECTORY));
965 instantList times = sortTimes(dirEntries, constantName);
966
967
968 // Get all processor directories
969 refPtr<dirIndexList> procDirs(lookupProcessorsPath(directory));
970 for (const dirIndex& dirIdx : procDirs())
971 {
972 const fileName& procDir = dirIdx.first();
973 fileName collDir(processorsPath(directory, procDir));
974 if (!collDir.empty() && collDir != directory)
975 {
976 fileNameList extraEntries
977 (
979 (
980 collDir,
982 )
983 );
984 mergeTimes
985 (
986 sortTimes(extraEntries, constantName),
987 constantName,
988 times
989 );
990 }
991 }
992
993 if (debug)
994 {
995 Pout<< "fileOperation::findTimes : Found times:" << times << endl;
996 }
997 return times;
998}
999
1000
1002(
1003 const IOobject& startIO,
1004 const scalar startValue,
1005 const word& stopInstance
1006) const
1007{
1008 const Time& time = startIO.time();
1009 IOobject io(startIO);
1010
1011 // Note: - if name is empty, just check the directory itself
1012 // - check both for isFile and headerOk since the latter does a
1013 // filePath so searches for the file.
1014 // - check for an object with local file scope (so no looking up in
1015 // parent directory in case of parallel)
1016
1017 if (exists(io))
1018 {
1020 << "Found exact match for \"" << io.name()
1021 << "\" in " << io.instance()/io.local()
1022 << endl;
1023
1024 return io;
1025 }
1026
1027 // Handling failures afterwards
1028 const bool exitIfMissing
1029 (
1030 startIO.readOpt() == IOobject::MUST_READ
1032 );
1033
1034 enum failureCodes { FAILED_STOPINST = 1, FAILED_CONSTINST = 2 };
1035 int failed(0);
1036
1037 instantList ts = time.times();
1038
1039 {
1040 label instIndex = ts.size()-1;
1041
1042 // Backward search for first time that is <= startValue
1043 for (; instIndex >= 0; --instIndex)
1044 {
1045 if (ts[instIndex].value() <= startValue)
1046 {
1047 break;
1048 }
1049 }
1050
1051 // Continue (forward) searching from here
1052 for (; instIndex >= 0; --instIndex)
1053 {
1054 io.instance() = ts[instIndex].name();
1055
1056 // Shortcut: if actual directory is the timeName we've
1057 // already tested it
1058 if
1059 (
1060 io.instance() == startIO.instance()
1061 && io.instance() != stopInstance
1062 )
1063 {
1064 continue;
1065 }
1066
1067 if (exists(io))
1068 {
1070 << "Found exact match for \"" << io.name()
1071 << "\" in " << io.instance()/io.local()
1072 << endl;
1073
1074 return io;
1075 }
1076
1077 // Check if hit minimum instance
1078 if (io.instance() == stopInstance)
1079 {
1081 << "Hit stopInstance " << stopInstance << endl;
1082
1083 if (exitIfMissing)
1084 {
1085 failed = failureCodes::FAILED_STOPINST;
1086 }
1087 else
1088 {
1089 return io;
1090 }
1091 break;
1092 }
1093 }
1094
1095
1096 // times() usually already includes the constant() so would
1097 // have been checked above. However, re-test under these conditions:
1098 // - times() is empty. Sometimes this can happen (e.g. decomposePar
1099 // with collated)
1100 // - times()[0] is not constant
1101 // - Times is empty.
1102 // Sometimes this can happen (eg, decomposePar with collated)
1103 // - Times[0] is not constant
1104 // - The startValue is negative (eg, kivaTest).
1105 // This plays havoc with the reverse search, causing it to miss
1106 // 'constant'
1107
1108 if
1109 (
1110 !failed
1111 && (ts.empty() || ts[0].name() != time.constant() || startValue < 0)
1112 )
1113 {
1114 io.instance() = time.constant();
1115
1116 if (exists(io))
1117 {
1119 << "Found constant match for \"" << io.name()
1120 << "\" in " << io.instance()/io.local()
1121 << endl;
1122
1123 return io;
1124 }
1125 }
1126 }
1127
1128
1129 if (!failed && exitIfMissing)
1130 {
1131 failed = failureCodes::FAILED_CONSTINST;
1132 }
1133
1134 // Handle failures
1135 // ~~~~~~~~~~~~~~~
1136
1137 if (failed)
1138 {
1139 FatalErrorInFunction << "Cannot find";
1140
1141 if (!io.name().empty())
1142 {
1144 << " file \"" << io.name() << "\" in";
1145 }
1146
1148 << " directory "
1149 << io.local() << " in times "
1150 << startIO.instance() << " down to ";
1151
1152 if (failed == failureCodes::FAILED_STOPINST)
1153 {
1154 FatalError << stopInstance;
1155 }
1156 else
1157 {
1158 FatalError << "constant";
1159 }
1161 }
1162
1163 return io;
1164}
1165
1166
1168(
1169 const objectRegistry& db,
1170 const fileName& instance,
1171 const fileName& local,
1172 word& newInstance
1173) const
1174{
1175 if (debug)
1176 {
1177 Pout<< "fileOperation::readObjects :"
1178 << " db:" << db.objectPath()
1179 << " instance:" << instance << endl;
1180 }
1181
1182 fileName path(db.path(instance, db.dbDir()/local));
1183
1184 newInstance = word::null;
1185 fileNameList objectNames;
1186
1187 if (Foam::isDir(path))
1188 {
1189 newInstance = instance;
1190 objectNames = Foam::readDir(path, fileName::FILE);
1191 }
1192 else
1193 {
1194 // Get processors equivalent of path
1195 fileName procsPath(filePath(path));
1196
1197 if (!procsPath.empty())
1198 {
1199 newInstance = instance;
1200 objectNames = Foam::readDir(procsPath, fileName::FILE);
1201 }
1202 }
1203 return objectNames;
1204}
1205
1206
1207void Foam::fileOperation::setNProcs(const label nProcs)
1208{}
1209
1210
1212(
1213 const fileName& dir,
1214 const fileName& local
1215) const
1216{
1217 label nProcs = 0;
1218 if (Pstream::master(comm_))
1219 {
1221
1222 // Detect any processorsDDD or processorDDD
1223 label maxProc = -1;
1224 for (const fileName& dirN : dirNames)
1225 {
1226 fileName rp, rd, rl;
1227 procRangeType group;
1228 label rNum;
1229
1230 const label readProci =
1231 splitProcessorPath(dirN, rp, rd, rl, group, rNum);
1232
1233 maxProc = max(maxProc, readProci);
1234 if (rNum != -1)
1235 {
1236 // Direct detection of processorsDDD
1237 maxProc = rNum-1;
1238 break;
1239 }
1240 }
1241 nProcs = maxProc+1;
1242
1243 if (nProcs == 0 && Foam::isDir(dir/processorsBaseDir))
1244 {
1246 << "Defunct collated naming: " << processorsBaseDir << nl
1247 << "Manually rename with the decomposition number. Eg,"
1248 << nl << nl
1249 << " mv processors processors16" << nl << nl
1250 << "...returning 1" << endl;
1251
1252 nProcs = 1;
1253 }
1254 }
1255 Pstream::broadcast(nProcs, comm_);
1256 return nProcs;
1257}
1258
1259
1261{
1262 if (debug)
1263 {
1264 Pout<< "fileOperation::flush : clearing processor directories cache"
1265 << endl;
1266 }
1267 procsDirs_.clear();
1268}
1269
1270
1272(
1273 const IOobject& io,
1274 const word& procsDir
1275) const
1276{
1277 return io.rootPath()/io.time().globalCaseName()/procsDir;
1278}
1279
1280
1282(
1283 const IOobject& io,
1284 const word& instance,
1285 const word& procsDir
1286) const
1287{
1288 return
1289 processorsCasePath(io, procsDir)
1290 /instance
1291 /io.db().dbDir()
1292 /io.local();
1293}
1294
1295
1297(
1298 const fileName& dir,
1299 const word& procsDir
1300) const
1301{
1302 // Check if directory is processorDDD
1303
1304 const word caseName(dir.name());
1305 if (caseName.starts_with("processor"))
1306 {
1307 // Reject both '^processor$' and '^processors.*$'
1308
1309 if (!std::isdigit(caseName[9]))
1310 {
1311 WarningInFunction << "Directory " << dir
1312 << " does not end in old-style processorDDD" << endl;
1313 }
1314
1315 return dir.path()/procsDir;
1316 }
1317
1318 return fileName::null;
1319}
1320
1321
1323(
1324 const fileName& objPath,
1325 fileName& path,
1326 fileName& procDir,
1327 fileName& local,
1328
1329 procRangeType& group,
1330 label& nProcs
1331)
1332{
1333 // Return value
1334 label returnProci = -1;
1335
1336 // Clear out the return parameters
1337
1338 path.clear();
1339 procDir.clear();
1340 local.clear();
1341 group.clear();
1342
1343 // Invalidate detected number of processors
1344 nProcs = -1;
1345
1346 // The local processor group is read as first/last, but stored as
1347 // start/size. Empty with start=0, size=0 if no range is detected
1348
1349
1350 // Start of 'processor..' directory name (the procDir)
1351 size_t pos = 0;
1352
1353 // The slash starting the trailing (local) directory
1354 size_t slashLocal = string::npos;
1355
1356
1357 // Search for processor at start of string or after /processor
1358 //
1359 // 'processor(\d+)'
1360 // 'processors(\d+)'
1361 // 'processors(\d+)_(\d+)-(\d+)'
1362
1363 for
1364 (
1365 /*nil*/;
1366 (pos = objPath.find("processor", pos)) != string::npos;
1367 pos += 9
1368 )
1369 {
1370 if (pos > 0 && objPath[pos-1] != '/')
1371 {
1372 // Not start of string or after /processor
1373 continue;
1374 }
1375
1376 // The parse point. One past 'processor'
1377 size_t firstp = pos + 9;
1378
1379 // normal: 'processor(\d+)'
1380 // plural: 'processors(\d+)'
1381
1382 const bool plural = (objPath[firstp] == 's');
1383
1384 if (plural)
1385 {
1386 ++firstp; // Skip over the 's'
1387 }
1388 else if (!std::isdigit(objPath[firstp]))
1389 {
1390 // Non-plural version (uncollated) requires digits only
1391 continue;
1392 }
1393
1394 // The next slash indicates there is a local directory
1395 slashLocal = objPath.find('/', firstp);
1396
1397 // The last parse point is the slash, or end of string
1398 const size_t lastp =
1399 (slashLocal == string::npos ? objPath.length() : slashLocal);
1400
1401 if (!std::isdigit(objPath[lastp-1]))
1402 {
1403 // Must end in a digit!
1404 // This traps entries that are too short or look quite wrong
1405 // and avoid a string to int conversion that will fail anyhow
1406 continue;
1407 }
1408
1409
1410 // Match: '^processors(\d+)$' -> nProcs
1411
1412 // Match: '^processors(\d+)_(\d+)-(\d+)$'
1413 // \1 = nProcs
1414 // \2 = beg processor group
1415 // \3 = end processor group (inclusive)
1416
1417 if (plural)
1418 {
1419 int nProcsRead = 0;
1420
1421 if
1422 (
1423 parseProcsNumRange
1424 (
1425 objPath.substr(firstp, lastp-firstp),
1426 nProcsRead,
1427 group
1428 )
1429 )
1430 {
1431 // Total number of processors
1432 nProcs = nProcsRead;
1433
1434 // We are done!
1435 break;
1436 }
1437 }
1438
1439 // Single
1440 // Match: '^processor(\d+)$' -> proci
1441
1442 label proci = 0;
1443 if
1444 (
1445 Foam::read(objPath.substr(firstp, lastp-firstp), proci)
1446 && (proci >= 0)
1447 )
1448 {
1449 // Capture value of an individual processor
1450 returnProci = proci;
1451
1452 // We are done!
1453 break;
1454 }
1455 }
1456
1457 if (pos != string::npos)
1458 {
1459 // The split succeeded, extract the components.
1460
1461 // The leading directory
1462 if (pos > 0)
1463 {
1464 path = objPath.substr(0, pos-1);
1465 }
1466
1467 // The slash starting the trailing (local) directory
1468 if (slashLocal != string::npos)
1469 {
1470 procDir = objPath.substr(pos, slashLocal-pos);
1471 local = objPath.substr(slashLocal+1);
1472 }
1473 else
1474 {
1475 procDir = objPath.substr(pos);
1476 }
1477 }
1478
1479 return returnProci;
1480}
1481
1482
1484{
1485 fileName path, pDir, local;
1486 procRangeType group;
1487 label nProcs;
1488 return splitProcessorPath(fName, path, pDir, local, group, nProcs);
1489}
1490
1491
1492// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
1493
1495{
1497 (
1499 );
1500}
1501
1502
1503// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
1504
1506{
1508 {
1509 word handler(getEnv("FOAM_FILEHANDLER"));
1510
1511 if (handler.empty())
1512 {
1514 }
1515
1517 }
1518
1520}
1521
1522
1525{
1526 if
1527 (
1528 newHandler
1530 && newHandler->type() == fileOperation::fileHandlerPtr_->type()
1531 )
1532 {
1533 return nullptr; // No change
1534 }
1535
1537
1538 fileOperation::fileHandlerPtr_ = std::move(newHandler);
1539
1540 return old;
1541}
1542
1543
1544// ************************************************************************* //
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: Enum.H:61
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition: HashTable.C:440
A List of objects of type <T> with automated input and output.
Definition: IOList.H:58
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:65
const Time & time() const
Return Time associated with the objectRegistry.
Definition: IOobject.C:506
readOption readOpt() const noexcept
The read option.
Definition: IOobjectI.H:164
bool typeHeaderOk(const bool checkType=true, const bool search=true, const bool verbose=true)
Read header (uses typeFilePath to find file) and check its info.
static Ostream & writeEndDivider(Ostream &os)
Write the standard end file divider.
const objectRegistry & db() const noexcept
Return the local objectRegistry.
Definition: IOobject.C:500
const fileName & local() const noexcept
Read access to local path component.
Definition: IOobjectI.H:208
const fileName & instance() const noexcept
Read access to instance path component.
Definition: IOobjectI.H:196
static fileCheckTypes fileModificationChecking
Type of file modification checking.
Definition: IOobject.H:303
fileName path() const
The complete path.
Definition: IOobject.C:524
const fileName & rootPath() const
Return the Time::rootPath()
Definition: IOobject.C:512
fileName objectPath() const
The complete path + object name.
Definition: IOobjectI.H:214
bool writeHeader(Ostream &os) const
Write header with current type()
@ MUST_READ_IF_MODIFIED
Definition: IOobject.H:180
The IOstreamOption is a simple container for options an IOstream can normally have.
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:233
An input stream of tokens.
Definition: ITstream.H:56
An interval of (signed) integers defined by a start and a size.
Definition: IntRange.H:64
void transfer(List< T > &list)
Definition: List.C:447
void setSize(const label n)
Alias for resize()
Definition: List.H:218
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
Generic output stream using a standard (STL) stream.
Definition: OSstream.H:57
label nProcs() const noexcept
Number of ranks associated with PstreamBuffers.
static void broadcast(Type &value, const label comm=UPstream::worldComm)
instantList times() const
Search the case for valid time directories.
Definition: TimePaths.C:149
const fileName & globalCaseName() const
Return global case name.
Definition: TimePathsI.H:56
const word & constant() const
Return constant name.
Definition: TimePathsI.H:96
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:80
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: Tuple2.H:58
const T1 & first() const noexcept
Return first.
Definition: Tuple2.H:118
const T2 & second() const noexcept
Return second.
Definition: Tuple2.H:130
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:94
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:329
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
iterator end() noexcept
Return an iterator to end traversing the UList.
Definition: UListI.H:350
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: autoPtr.H:66
Checking for changes to files.
Definition: fileMonitor.H:67
fileState
Enumeration defining the file state.
Definition: fileMonitor.H:74
A class for handling file names.
Definition: fileName.H:76
@ FILE
A regular file.
Definition: fileName.H:83
@ DIRECTORY
A directory.
Definition: fileName.H:84
static const fileName null
An empty fileName.
Definition: fileName.H:102
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:176
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:199
An encapsulation of filesystem-related operations.
Definition: fileOperation.H:69
virtual void addWatches(regIOobject &, const fileNameList &) const
Helper: add watches for list of regIOobjects.
static bool isFileOrDir(const bool isFile, const fileName &)
Helper: check for file (isFile) or directory (!isFile)
bool distributed() const noexcept
Distributed roots (parallel run)
virtual void setNProcs(const label nProcs)
Set number of processor directories/results. Only used in.
refPtr< dirIndexList > lookupAndCacheProcessorsPath(const fileName &objectPath, const bool syncPar) const
Lookup name of processorsDDD using cache.
static instantList sortTimes(const fileNameList &dirEntries, const word &constantName="constant")
Sort directory entries according to time value,.
virtual void flush() const
Forcibly wait until all output done. Flush any cached data.
virtual bool removeWatch(const label) const
Remove watch on a file (using handle)
virtual fileNameList readObjects(const objectRegistry &db, const fileName &instance, const fileName &local, word &newInstance) const
Search directory for objects. Used in IOobjectList.
static word defaultFileHandler
Name of the default fileHandler.
virtual label findWatch(const labelList &watchIndices, const fileName &) const
Find index (or -1) of file in list of handles.
fileName processorsCasePath(const IOobject &, const word &procDir) const
Generate path (like io.path) from root+casename with any.
virtual refPtr< dirIndexList > lookupProcessorsPath(const fileName &objectPath) const
Lookup name of processorsDDD using cache.
virtual IOobject findInstance(const IOobject &io, const scalar startValue, const word &stopInstance) const
Find instance where IOobject is.
static label splitProcessorPath(const fileName &objectPath, fileName &path, fileName &procDir, fileName &local, procRangeType &group, label &nProcs)
Split objectPath into part before 'processor' and part after.
static autoPtr< fileOperation > NewUncollated()
Static construct the commonly used uncollatedFileOperation.
static label detectProcessorPath(const fileName &objPath)
Detect processor number from '/aa/bb/processorDDD/cc'.
pathType
Enumeration for the location of an IOobject.
Definition: fileOperation.H:74
static labelList ioRanks()
Retrieve list of IO ranks from FOAM_IORANKS env variable.
virtual fileMonitor::fileState getState(const label) const
Get current state of file (using handle)
virtual void setUnmodified(const label) const
Set current state of file (using handle) to unmodified.
virtual void updateStates(const bool masterOnly, const bool syncPar) const
Update state of all files.
static const Enum< pathType > pathTypeNames_
Definition: fileOperation.H:95
fileMonitor & monitor() const
Get or create fileMonitor singleton.
static autoPtr< fileOperation > fileHandlerPtr_
Static fileOperation.
fileName processorsPath(const IOobject &, const word &instance, const word &procDir) const
Generate path (like io.path) with provided instance and any.
virtual instantList findTimes(const fileName &, const word &) const
Get sorted list of times.
static word processorsBaseDir
Return the processors directory name (usually "processors")
virtual bool writeObject(const regIOobject &io, IOstreamOption streamOpt=IOstreamOption(), const bool valid=true) const
Writes a regIOobject (so header, contents and divider).
virtual fileName getFile(const label) const
Get name of file being watched (using handle)
static void mergeTimes(const instantList &extraTimes, const word &constantName, instantList &times)
Merge two times.
fileOperation that assumes file operations are local.
@ LITERAL
String literal.
Definition: keyType.H:81
Registry of regIOobjects.
virtual const fileName & dbDir() const
Local directory path of this objectRegistry relative to the time.
virtual fileName filePath() const
Return complete path + object name if the file exists.
A class for managing references or pointers (no reference counting)
Definition: refPtr.H:58
bool exists() const noexcept
True if expression is non-empty.
Definition: regExpCxxI.H:139
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition: regIOobject.H:76
const labelList & watchIndices() const noexcept
Read access to file-monitoring handles.
Definition: regIOobjectI.H:202
splitCell * master() const
Definition: splitCell.H:113
bool starts_with(const std::string &s) const
True if string starts with the given prefix (cf. C++20)
Definition: string.H:297
virtual void addWatch()
Add file watch on object (READ_IF_MODIFIED)
static constexpr uint64_t npos
Out of range position or size.
A class for handling words, derived from Foam::string.
Definition: word.H:68
static const word null
An empty word.
Definition: word.H:80
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
regionProperties rp(runTime)
#define FatalErrorInLookup(lookupTag, lookupName, lookupTable)
Report an error message using Foam::FatalError.
Definition: error.H:457
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
IOobject io("surfaceFilmProperties", mesh.time().constant(), mesh, IOobject::READ_IF_PRESENT, IOobject::NO_WRITE, false)
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
#define DebugInFunction
Report an information message using Foam::Info.
constexpr const char *const group
Group name for atomic constants.
dictionary & optimisationSwitches()
The OptimisationSwitches sub-dictionary in the central controlDict(s).
Definition: debug.C:219
Namespace for OpenFOAM.
const fileOperation & fileHandler()
Get current file handler.
dimensionedScalar pos(const dimensionedScalar &ds)
string getEnv(const std::string &envName)
Get environment value for given envName.
Definition: MSwindows.C:371
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:108
bool exists(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist (as DIRECTORY or FILE) in the file system?
Definition: MSwindows.C:633
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:515
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
errorManip< error > abort(error &err)
Definition: errorManip.H:144
fileNameList readDir(const fileName &directory, const fileName::Type type=fileName::FILE, const bool filtergz=true, const bool followLink=true)
Read a directory and return the entries as a fileName List.
Definition: MSwindows.C:715
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: MSwindows.C:666
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: MSwindows.C:651
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList f(nPoints)
#define defineRunTimeSelectionTable(baseType, argNames)
Define run-time selection table.
volScalarField & b
Definition: createFields.H:27
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
Specialized string sorting.
A list compare binary predicate for normal sort.
Definition: UList.H:188