From c9333a5ac8a4b6c9792b20af85f08ae2e137a468 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 1 Nov 2021 12:36:45 +0100
Subject: [PATCH] ENH: improve flexibility of error, messageStream output

- provide a plain stream() method on messageStream to reduce reliance
  on casting operators and slightly opaque operator()() calls etc

- support alternative stream for messageStream serial output.
  This can be used to support local redirection of output.
  For example,

     refPtr<OFstream> logging;   // or autoPtr, unique_ptr etc

     // Later...
     Info.stream(logging.get())
        << "Detailed output ..." << endl;

  This will use the stdout semantics in the normal case, or allow
  redirection to an output file if a target output stream is defined,
  but still effectively use /dev/null on non-master processes.

  This is mostly the same as this ternary

      (logging ? *logging : Info())

  except that the ternary could be incorrect on sub-processes,
  requires more typing etc.

ENH: use case-relative names of dictionary, IOstream for FatalIOError

- normally yields more easily understandable information
---
 .../test/foamVersion/Test-foamVersion.C       |   4 +-
 .../test/plotFunction1/Test-plotFunction1.C   |   2 +-
 applications/test/wmake1/newStub.C            |   2 +-
 .../db/IOstreams/Sstreams/prefixOSstream.C    |   2 +-
 .../db/IOstreams/Sstreams/prefixOSstream.H    |   4 +-
 src/OpenFOAM/db/Time/TimeIO.C                 |   7 +-
 src/OpenFOAM/db/error/IOerror.C               |  53 +++++-
 src/OpenFOAM/db/error/error.C                 |  58 ++++---
 src/OpenFOAM/db/error/error.H                 |  92 ++++++++---
 src/OpenFOAM/db/error/messageStream.C         | 155 ++++++++++--------
 src/OpenFOAM/db/error/messageStream.H         | 117 +++++++------
 src/OpenFOAM/global/argList/argList.C         |   4 +-
 src/OpenFOAM/global/argList/argListHelp.C     |   4 +-
 .../GAMGAgglomeration/GAMGAgglomeration.C     |   6 +-
 .../snappyHexMeshDriver/snappyLayerDriver.C   |   6 +-
 .../bridge/code/polynomial-motion.C           |   4 +-
 .../building/code/building-motion.C           |   4 +-
 17 files changed, 337 insertions(+), 187 deletions(-)

diff --git a/applications/test/foamVersion/Test-foamVersion.C b/applications/test/foamVersion/Test-foamVersion.C
index 9588954bc2d..4c5cb405627 100644
--- a/applications/test/foamVersion/Test-foamVersion.C
+++ b/applications/test/foamVersion/Test-foamVersion.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,7 +52,7 @@ void testExtraction(const std::string& str)
 int main()
 {
     Info<< "\nVersion information (function)" << nl;
-    foamVersion::printBuildInfo(Info().stdStream());
+    foamVersion::printBuildInfo(Info.stdStream());
 
     Info
         << "\nVersion information (macros)" << nl
diff --git a/applications/test/plotFunction1/Test-plotFunction1.C b/applications/test/plotFunction1/Test-plotFunction1.C
index 99bc8769a80..dfb2c407bdc 100644
--- a/applications/test/plotFunction1/Test-plotFunction1.C
+++ b/applications/test/plotFunction1/Test-plotFunction1.C
@@ -69,7 +69,7 @@ int main(int argc, char *argv[])
     const scalar incrTime = args.getOrDefault<scalar>("incr", 0.1);
     const scalar timeBase = args.getOrDefault<scalar>("timeBase", 1);
 
-    Info().precision(10);
+    Info.stream().precision(10);
 
     for (label argi=1; argi < args.size(); ++argi)
     {
diff --git a/applications/test/wmake1/newStub.C b/applications/test/wmake1/newStub.C
index 480c5958081..8f830efb498 100644
--- a/applications/test/wmake1/newStub.C
+++ b/applications/test/wmake1/newStub.C
@@ -8,6 +8,6 @@ namespace Foam
     void printTest()
     {
         Info<< nl;
-        foamVersion::printBuildInfo(Info().stdStream());
+        foamVersion::printBuildInfo(Info.stdStream());
     }
 }
diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C
index d274d746b23..ccb16b613fd 100644
--- a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C
+++ b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.C
@@ -34,7 +34,7 @@ License
 
 inline void Foam::prefixOSstream::checkWritePrefix()
 {
-    if (printPrefix_ && prefix_.size())
+    if (printPrefix_ && !prefix_.empty())
     {
         OSstream::write(prefix_.c_str());
         printPrefix_ = false;
diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.H b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.H
index 1cdf23ec0ef..f84e1dbd91f 100644
--- a/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.H
+++ b/src/OpenFOAM/db/IOstreams/Sstreams/prefixOSstream.H
@@ -99,13 +99,13 @@ public:
         // Enquiry
 
             //- Return the stream prefix
-            const string& prefix() const
+            const string& prefix() const noexcept
             {
                 return prefix_;
             }
 
             //- Return non-const access to the stream prefix
-            string& prefix()
+            string& prefix() noexcept
             {
                 return prefix_;
             }
diff --git a/src/OpenFOAM/db/Time/TimeIO.C b/src/OpenFOAM/db/Time/TimeIO.C
index cc95ef4bf19..cb7afcb7855 100644
--- a/src/OpenFOAM/db/Time/TimeIO.C
+++ b/src/OpenFOAM/db/Time/TimeIO.C
@@ -390,11 +390,8 @@ void Foam::Time::readDict()
         Pout.precision(IOstream::defaultPrecision());
         Perr.precision(IOstream::defaultPrecision());
 
-        FatalError().precision(IOstream::defaultPrecision());
-        FatalIOError.error::operator()().precision
-        (
-            IOstream::defaultPrecision()
-        );
+        FatalError.stream().precision(IOstream::defaultPrecision());
+        FatalIOError.stream().precision(IOstream::defaultPrecision());
     }
 
     if (controlDict_.found("writeCompression"))
diff --git a/src/OpenFOAM/db/error/IOerror.C b/src/OpenFOAM/db/error/IOerror.C
index 93acd448e38..ec1c7836d2b 100644
--- a/src/OpenFOAM/db/error/IOerror.C
+++ b/src/OpenFOAM/db/error/IOerror.C
@@ -27,6 +27,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "error.H"
+#include "argList.H"
 #include "StringStream.H"
 #include "fileName.H"
 #include "dictionary.H"
@@ -71,12 +72,18 @@ Foam::OSstream& Foam::IOerror::operator()
     const label ioEndLineNumber
 )
 {
-    error::operator()(functionName, sourceFileName, sourceFileLineNumber);
+    OSstream& os = error::operator()
+    (
+        functionName,
+        sourceFileName,
+        sourceFileLineNumber
+    );
+
     ioFileName_ = ioFileName;
     ioStartLineNumber_ = ioStartLineNumber;
     ioEndLineNumber_ = ioEndLineNumber;
 
-    return operator OSstream&();
+    return os;
 }
 
 
@@ -93,9 +100,9 @@ Foam::OSstream& Foam::IOerror::operator()
         functionName,
         sourceFileName,
         sourceFileLineNumber,
-        ioStream.name(),
+        argList::envRelativePath(ioStream.name()),
         ioStream.lineNumber(),
-        -1
+        -1  // No known endLineNumber
     );
 }
 
@@ -113,7 +120,43 @@ Foam::OSstream& Foam::IOerror::operator()
         functionName,
         sourceFileName,
         sourceFileLineNumber,
-        dict.name(),
+        dict.relativeName(),
+        dict.startLineNumber(),
+        dict.endLineNumber()
+    );
+}
+
+
+Foam::OSstream& Foam::IOerror::operator()
+(
+    const std::string& where,
+    const IOstream& ioStream
+)
+{
+    return operator()
+    (
+        where.c_str(),
+        "",     // No source file
+        1,      // Non-zero to ensure that 'where' is reported
+        argList::envRelativePath(ioStream.name()),
+        ioStream.lineNumber(),
+        -1      // No known endLineNumber
+    );
+}
+
+
+Foam::OSstream& Foam::IOerror::operator()
+(
+    const std::string& where,
+    const dictionary& dict
+)
+{
+    return operator()
+    (
+        where.c_str(),
+        "",     // No source file
+        1,      // Non-zero to ensure that 'where' is reported
+        dict.relativeName(),
         dict.startLineNumber(),
         dict.endLineNumber()
     );
diff --git a/src/OpenFOAM/db/error/error.C b/src/OpenFOAM/db/error/error.C
index 09920fe1298..50aebea8160 100644
--- a/src/OpenFOAM/db/error/error.C
+++ b/src/OpenFOAM/db/error/error.C
@@ -27,11 +27,11 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "error.H"
-#include "StringStream.H"
 #include "fileName.H"
 #include "dictionary.H"
 #include "JobInfo.H"
 #include "Pstream.H"
+#include "StringStream.H"
 #include "foamVersion.H"
 #include "OSspecific.H"
 #include "Switch.H"
@@ -47,13 +47,12 @@ bool Foam::error::warnAboutAge(const int version) noexcept
 
 bool Foam::error::warnAboutAge(const char* what, const int version)
 {
-    // No warning for 0 (unversioned) or -ve values (silent versioning)
-    const bool old = ((version > 0) && (version < foamVersion::api));
-
-    // Note:
-    // No warning for (version >= foamVersion::api), which
+    // No warning for 0 (unversioned) or -ve values (silent versioning).
+    // Also no warning for (version >= foamVersion::api), which
     // can be used to denote future expiry dates of transition features.
 
+    const bool old = ((version > 0) && (version < foamVersion::api));
+
     if (old)
     {
         const int months =
@@ -155,11 +154,22 @@ Foam::OSstream& Foam::error::operator()
     const int sourceFileLineNumber
 )
 {
-    functionName_ = functionName;
-    sourceFileName_ = sourceFileName;
+    functionName_.clear();
+    sourceFileName_.clear();
+
+    if (functionName)
+    {
+        // With nullptr protection
+        functionName_.assign(functionName);
+    }
+    if (sourceFileName)
+    {
+        // With nullptr protection
+        sourceFileName_.assign(sourceFileName);
+    }
     sourceFileLineNumber_ = sourceFileLineNumber;
 
-    return operator OSstream&();
+    return this->stream();
 }
 
 
@@ -179,20 +189,6 @@ Foam::OSstream& Foam::error::operator()
 }
 
 
-Foam::error::operator Foam::OSstream&()
-{
-    if (!messageStreamPtr_->good())
-    {
-        Perr<< nl
-            << "error::operator OSstream&() : error stream has failed"
-            << endl;
-        abort();
-    }
-
-    return *messageStreamPtr_;
-}
-
-
 Foam::error::operator Foam::dictionary() const
 {
     dictionary errDict;
@@ -209,6 +205,7 @@ Foam::error::operator Foam::dictionary() const
     return errDict;
 }
 
+
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 void Foam::error::exiting(const int errNo, const bool isAbort)
@@ -290,6 +287,21 @@ void Foam::error::simpleExit(const int errNo, const bool isAbort)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+Foam::OSstream& Foam::error::stream()
+{
+    // Don't need (messageStreamPtr_) check - always allocated
+    if (!messageStreamPtr_->good())
+    {
+        Perr<< nl
+            << "error::stream() : error stream has failed"
+            << endl;
+        abort();
+    }
+
+    return *messageStreamPtr_;
+}
+
+
 Foam::string Foam::error::message() const
 {
     return messageStreamPtr_->str();
diff --git a/src/OpenFOAM/db/error/error.H b/src/OpenFOAM/db/error/error.H
index 2bd569e7eef..931896bf071 100644
--- a/src/OpenFOAM/db/error/error.H
+++ b/src/OpenFOAM/db/error/error.H
@@ -64,6 +64,9 @@ SeeAlso
 namespace Foam
 {
 
+// Forward Declarations
+class OStringStream;
+
 /*---------------------------------------------------------------------------*\
                             Class error Declaration
 \*---------------------------------------------------------------------------*/
@@ -138,16 +141,19 @@ public:
         //- Clear any messages
         void clear() const;
 
+        //- The currently defined function name for output messages
         const string& functionName() const noexcept
         {
             return functionName_;
         }
 
+        //- The currently defined source-file name for output messages
         const string& sourceFileName() const noexcept
         {
             return sourceFileName_;
         }
 
+        //- The currently defined source-file line number for output messages
         label sourceFileLineNumber() const noexcept
         {
             return sourceFileLineNumber_;
@@ -168,15 +174,33 @@ public:
             return old;
         }
 
+
+    // Output
+
+        //- Return OSstream for output operations
+        OSstream& stream();
+
+        //- Implicit cast to OSstream for << operations
+        operator OSstream&()
+        {
+            return this->stream();
+        }
+
+        //- Explicit convert to OSstream for << operations
+        OSstream& operator()()
+        {
+            return this->stream();
+        }
+
         //- Define basic print message
-        //  \return OSstream for further info.
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const string& functionName
         );
 
         //- Define basic print message
-        //  \return OSstream for further info.
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -185,7 +209,7 @@ public:
         );
 
         //- Define basic print message
-        //  \return OSstream for further info.
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const string& functionName,
@@ -193,16 +217,10 @@ public:
             const int sourceFileLineNumber = 0
         );
 
-        //- Explicit convert to OSstream for << operations
-        OSstream& operator()()
-        {
-            return operator OSstream&();
-        }
 
-        //- Convert to OSstream
-        operator OSstream&();
+    // Other
 
-        //- Return a dictionary representation of the error
+        //- Extract a dictionary representation of the error information
         operator dictionary() const;
 
 
@@ -216,6 +234,7 @@ public:
         //- True if FOAM_ABORT is on.
         static bool useAbort();
 
+
         //- Exit : can be called for any error to exit program.
         //  Redirects to abort() when FOAM_ABORT is on.
         void exit(const int errNo = 1);
@@ -285,23 +304,29 @@ public:
 
     // Member Functions
 
+        //- The currently defined IO name for output messages
         const string& ioFileName() const noexcept
         {
             return ioFileName_;
         }
 
+        //- The currently defined IO start-line number for output messages
         label ioStartLineNumber() const noexcept
         {
             return ioStartLineNumber_;
         }
 
+        //- The currently defined IO end-line number
         label ioEndLineNumber() const noexcept
         {
             return ioEndLineNumber_;
         }
 
-        //- Convert to OSstream
-        //  Prints basic message and returns OSstream for further info.
+
+    // Output
+
+        //- Define basic print message
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -312,8 +337,8 @@ public:
             const label ioEndLineNumber = -1
         );
 
-        //- Convert to OSstream
-        //  Prints basic message and returns OSstream for further info.
+        //- Define basic print message for IO stream
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -322,8 +347,9 @@ public:
             const IOstream& ioStream
         );
 
-        //- Convert to OSstream
-        //  Prints basic message and returns OSstream for further info.
+        //- Define basic print message for dictionary entries.
+        //- Uses the dictionary::relativeName() on output.
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -332,6 +358,28 @@ public:
             const dictionary& dict
         );
 
+
+        //- Define basic print message for IO stream,
+        //- independent of a source-file reference.
+        //- Uses the dictionary::relativeName() on output.
+        //  \return OSstream for further operations
+        OSstream& operator()
+        (
+            const std::string& where,
+            const IOstream& ioStream
+        );
+
+        //- Define basic print message for dictionary entries,
+        //- independent of a source-file reference.
+        //- Uses the dictionary::relativeName() on output.
+        //  \return OSstream for further operations
+        OSstream& operator()
+        (
+            const std::string& where,
+            const dictionary& dict
+        );
+
+
         //- Print basic message and exit.
         //  Uses cerr if streams not yet constructed (at startup).
         //  Use in startup parsing instead of FatalError.
@@ -344,9 +392,11 @@ public:
             const string& msg
         );
 
-        //- Return a dictionary representation of the error
-        operator dictionary() const;
 
+    // Other
+
+        //- Extract a dictionary representation of the error information
+        operator dictionary() const;
 
         //- Exit : can be called for any error to exit program
         void exit(const int errNo = 1);
@@ -371,11 +421,11 @@ Ostream& operator<<(Ostream& os, const IOerror& err);
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 // Global error declarations: defined in error.C
 
-//- Error stream (uses stdout - output on all processes),
+//- Error stream (stdout output on all processes),
 //- with additional 'FOAM FATAL ERROR' header text and stack trace.
 extern error   FatalError;
 
-//- Error stream (uses stdout - output on all processes),
+//- Error stream (stdout output on all processes),
 //- with additional 'FOAM FATAL IO ERROR' header text and stack trace.
 extern IOerror FatalIOError;
 
diff --git a/src/OpenFOAM/db/error/messageStream.C b/src/OpenFOAM/db/error/messageStream.C
index 4e12142f6ce..c0fdd7d6c7a 100644
--- a/src/OpenFOAM/db/error/messageStream.C
+++ b/src/OpenFOAM/db/error/messageStream.C
@@ -68,6 +68,75 @@ Foam::messageStream::messageStream(const dictionary& dict)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+Foam::OSstream& Foam::messageStream::stream(OSstream* alternative)
+{
+    if (level)
+    {
+        // Serlal (master only) output?
+        const bool serialOnly
+        (
+            (
+                severity_ == INFO
+             || severity_ == INFO_STDERR
+             || severity_ == WARNING
+            )
+         || !Pstream::parRun()
+        );
+
+        if (serialOnly && (Pstream::parRun() && !Pstream::master()))
+        {
+            return Snull; // Non-serial, non-master: exit early
+        }
+
+
+        // Use stderr instead of stdout:
+        // - requested via static <redirect> variable
+        // - explicit:  INFO_STDERR
+        // - inferred:  WARNING -> stderr when infoDetailLevel == 0
+        const bool useStderr =
+        (
+            (redirect == 2)
+         || (severity_ == INFO_STDERR)
+         || (severity_ == WARNING && Foam::infoDetailLevel == 0)
+        );
+
+        OSstream* osptr;
+
+        if (serialOnly)
+        {
+            // Use supplied alternative? Valid for serial only
+            osptr = alternative;
+
+            if (!osptr)
+            {
+                osptr = (useStderr ? &Serr : &Sout);
+            }
+        }
+        else
+        {
+            // Non-serial
+            osptr = (useStderr ? &Perr : &Pout);
+        }
+
+        if (!title_.empty())
+        {
+            (*osptr) << title_.c_str();
+        }
+
+        if (maxErrors_ && (++errorCount_ >= maxErrors_))
+        {
+            FatalErrorInFunction
+                << "Too many errors..."
+                << abort(FatalError);
+        }
+
+        return *osptr;
+    }
+
+    return Snull;
+}
+
+
 Foam::OSstream& Foam::messageStream::masterStream(const label communicator)
 {
     if (UPstream::warnComm != -1 && communicator != UPstream::warnComm)
@@ -78,13 +147,19 @@ Foam::OSstream& Foam::messageStream::masterStream(const label communicator)
 
     if (communicator == UPstream::worldComm || UPstream::master(communicator))
     {
-        return operator()();
+        return this->stream();
     }
 
     return Snull;
 }
 
 
+std::ostream& Foam::messageStream::stdStream()
+{
+    return this->stream().stdStream();
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 Foam::OSstream& Foam::messageStream::operator()
@@ -92,10 +167,13 @@ Foam::OSstream& Foam::messageStream::operator()
     const string& functionName
 )
 {
-    OSstream& os = operator OSstream&();
+    OSstream& os = this->stream();
 
-    os  << nl
-        << "    From " << functionName.c_str() << nl;
+    if (!functionName.empty())
+    {
+        os  << nl
+            << "    From " << functionName.c_str() << nl;
+    }
 
     return os;
 }
@@ -108,7 +186,7 @@ Foam::OSstream& Foam::messageStream::operator()
     const int sourceFileLineNumber
 )
 {
-    OSstream& os = operator OSstream&();
+    OSstream& os = this->stream();
 
     os  << nl
         << "    From " << functionName << nl
@@ -146,7 +224,7 @@ Foam::OSstream& Foam::messageStream::operator()
     const label ioEndLineNumber
 )
 {
-    OSstream& os = operator OSstream&();
+    OSstream& os = this->stream();
 
     os  << nl
         << "    From " << functionName << nl
@@ -185,7 +263,7 @@ Foam::OSstream& Foam::messageStream::operator()
         sourceFileLineNumber,
         ioStream.name(),
         ioStream.lineNumber(),
-        -1
+        -1  // No known endLineNumber
     );
 }
 
@@ -203,82 +281,29 @@ Foam::OSstream& Foam::messageStream::operator()
         functionName,
         sourceFileName,
         sourceFileLineNumber,
-        dict.name(),
+        dict.relativeName(),
         dict.startLineNumber(),
         dict.endLineNumber()
     );
 }
 
 
-Foam::messageStream::operator Foam::OSstream&()
-{
-    if (level)
-    {
-        const bool collect =
-        (
-            severity_ == INFO
-         || severity_ == WARNING
-         || severity_ == INFO_STDERR
-        );
-
-        // Could add guard with parRun
-        if (collect && !Pstream::master())
-        {
-            return Snull;
-        }
-
-        // Use stderr instead of stdout
-        // - INFO_STDERR
-        // - WARNING when infoDetailLevel == 0
-        const bool useStderr =
-        (
-            (severity_ == INFO_STDERR)
-         || (severity_ == WARNING && Foam::infoDetailLevel == 0)
-        );
-
-        OSstream& os =
-        (
-            (collect || !Pstream::parRun())
-          ? (useStderr ? Serr : Sout)
-          : (useStderr ? Perr : Pout)
-        );
-
-
-        if (!title().empty())
-        {
-            os << title().c_str();
-        }
-
-        if (maxErrors_ && (++errorCount_ >= maxErrors_))
-        {
-            FatalErrorInFunction
-                << "Too many errors"
-                << abort(FatalError);
-        }
-
-        return os;
-    }
-
-    return Snull;
-}
-
-
 // * * * * * * * * * * * * * * * Global Variables  * * * * * * * * * * * * * //
 
-Foam::messageStream Foam::Info("", messageStream::INFO);
+Foam::messageStream Foam::Info("", Foam::messageStream::INFO);
 
-Foam::messageStream Foam::InfoErr("", messageStream::INFO_STDERR);
+Foam::messageStream Foam::InfoErr("", Foam::messageStream::INFO_STDERR);
 
 Foam::messageStream Foam::Warning
 (
     "--> FOAM Warning : ",
-    messageStream::WARNING
+    Foam::messageStream::WARNING
 );
 
 Foam::messageStream Foam::SeriousError
 (
     "--> FOAM Serious Error : ",
-    messageStream::SERIOUS,
+    Foam::messageStream::SERIOUS,
     100
 );
 
diff --git a/src/OpenFOAM/db/error/messageStream.H b/src/OpenFOAM/db/error/messageStream.H
index 034f90047e2..cadd11688d1 100644
--- a/src/OpenFOAM/db/error/messageStream.H
+++ b/src/OpenFOAM/db/error/messageStream.H
@@ -28,19 +28,22 @@ Class
     Foam::messageStream
 
 Description
-    Class to handle messaging in a simple, consistent stream-based
-    manner.
+    Handle output messages in a simple, consistent stream-based manner.
 
-    The messageStream class is globally instantiated with a title string and
-    a severity (which controls the program termination) and a number of
-    errors before termination.  Errors, messages and other data are sent to
-    the messageStream class in the standard manner.
+    The messageStream class is globally instantiated with a title
+    string and a severity (which controls the program termination),
+    optionally with a max number of errors before termination.
 
-Usage
-    \code
-        messageStream
-            << "message1" << "message2" << FoamDataType << endl;
-    \endcode
+    Errors, messages and other data are sent to the messageStream class in
+    the standard manner.
+
+    For parallel applications, the output for 'standard' messages
+    (Info, Warnings) is effectively suppressed on all sub-processes,
+    which results in a single output message instead of a flood of output
+    messages from each process.  The error type of messages do, however,
+    retain output on all processes, which ensures that parallel termination
+    occurs correctly and the source of the problem is properly traceable to
+    the originating processor.
 
 SourceFiles
     messageStream.C
@@ -52,6 +55,7 @@ SourceFiles
 
 #include "label.H"
 #include "string.H"
+#include <iostream>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -60,9 +64,7 @@ namespace Foam
 
 // Forward Declarations
 class IOstream;
-class Ostream;
 class OSstream;
-class OStringStream;
 class dictionary;
 
 /*---------------------------------------------------------------------------*\
@@ -73,14 +75,17 @@ class messageStream
 {
 public:
 
-    //- Message type, or error severity flags
+    //- Message type, error severity flags
     enum errorSeverity
     {
-        INFO = 1,   //!< General information output
-        WARNING,    //!< Warning of possible problem.
-        SERIOUS,    //!< A serious problem - eg, data corruption.
-        FATAL,      //!< A fatal error.
-        INFO_STDERR = INFO | 0x10,  //!< Information, but on stderr
+        // Serial-only output:
+        INFO = 1,       //!< General information output (stdout)
+        INFO_STDERR,    //!< General information output (stderr)
+        WARNING,        //!< Warning of possible problem.
+
+        // Parallel-aware output:
+        SERIOUS,        //!< A serious problem - eg, data corruption.
+        FATAL           //!< A fatal error.
     };
 
 
@@ -150,19 +155,43 @@ public:
             return old;
         }
 
-        //- Convert to OSstream
-        //  Prints to Pout for the master stream
+
+    // Output
+
+        //- Return OSstream for output operations.
+        //- Use the \c alternative stream for serial-only output
+        //- if it is a valid pointer.
+        OSstream& stream(OSstream* alternative = nullptr);
+
+        //- Return OSstream for output operations on the master process only,
+        //- Snull on other processes.
         OSstream& masterStream(const label communicator);
 
-        //- Print basic message
-        //  \return OSstream for further info.
+        //- Return std::ostream for output operations.
+        std::ostream& stdStream();
+
+        //- Implicit cast to OSstream for << operations
+        operator OSstream&()
+        {
+            return this->stream();
+        }
+
+        //- Explicitly convert to OSstream for << operations
+        OSstream& operator()()
+        {
+            return this->stream();
+        }
+
+        //- Report 'From function-name'
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const string& functionName
         );
 
+        //- Report 'From function-name, source file, line number'
         //- Print basic message
-        //  \return OSstream for further info.
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -170,8 +199,8 @@ public:
             const int sourceFileLineNumber = 0
         );
 
-        //- Print basic message
-        //  \return OSstream for further info.
+        //- Report 'From function-name, source file, line number'
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const string& functionName,
@@ -179,8 +208,9 @@ public:
             const int sourceFileLineNumber = 0
         );
 
-        //- Print basic message
-        //  \return OSstream for further info.
+        //- Report 'From function-name, source file, line number'
+        //- as well as io-file name and location
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
@@ -191,34 +221,27 @@ public:
             const label ioEndLineNumber = -1
         );
 
-        //- Print basic message
-        //  \return OSstream for further info.
+        //- Report 'From function-name, source file, line number'
+        //- as well as io-file name and location
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
             const char* sourceFileName,
             const int sourceFileLineNumber,
-            const IOstream&
+            const IOstream&  //!< Provides name and lineNumber
         );
 
-        //- Print basic message
-        //  \return OSstream for further info.
+        //- Report 'From function-name, source file, line number'
+        //- as well as io-file name and location
+        //  \return OSstream for further operations
         OSstream& operator()
         (
             const char* functionName,
             const char* sourceFileName,
             const int sourceFileLineNumber,
-            const dictionary&
+            const dictionary&  //!< Provides name, startLine, endLine
         );
-
-        //- Convert to OSstream for << operations
-        operator OSstream&();
-
-        //- Explicitly convert to OSstream for << operations
-        OSstream& operator()()
-        {
-            return operator OSstream&();
-        }
 };
 
 
@@ -238,17 +261,17 @@ public:
 //  \note This flag is initialized to 1 by default.
 extern int infoDetailLevel;
 
-//- Information stream (uses stdout - output is on the master only)
+//- Information stream (stdout output on master, null elsewhere)
 extern messageStream Info;
 
-//- Information stream (uses stderr - output is on the master only)
+//- Information stream (stderr output on master, null elsewhere)
 extern messageStream InfoErr;
 
-//- Warning stream (uses stdout - output is on the master only),
+//- Warning stream (stdout output on master, null elsewhere),
 //- with additional 'FOAM Warning' header text.
 extern messageStream Warning;
 
-//- Error stream (uses stdout - output on all processes),
+//- Error stream (stdout output on all processes),
 //- with additional 'FOAM Serious Error' header text.
 extern messageStream SeriousError;
 
diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C
index b500f8e895d..ddeb11f77f7 100644
--- a/src/OpenFOAM/global/argList/argList.C
+++ b/src/OpenFOAM/global/argList/argList.C
@@ -873,7 +873,7 @@ Foam::argList::argList
                 ++argi;
                 if (argi >= args_.size())
                 {
-                    foamVersion::printBuildInfo(Info().stdStream(), false);
+                    foamVersion::printBuildInfo(Info.stdStream(), false);
 
                     Info<< nl
                         <<"Error: option '-" << optName
@@ -1038,7 +1038,7 @@ void Foam::argList::parse
     // Print the collected error messages and exit if check fails
     if (!check(checkArgs, checkOpts))
     {
-        foamVersion::printBuildInfo(Info().stdStream(), false);
+        foamVersion::printBuildInfo(Info.stdStream(), false);
         FatalError.write(Info, false);
 
         Pstream::exit(1); // works for serial and parallel
diff --git a/src/OpenFOAM/global/argList/argListHelp.C b/src/OpenFOAM/global/argList/argListHelp.C
index a1ebec28cca..883fbebb93b 100644
--- a/src/OpenFOAM/global/argList/argListHelp.C
+++ b/src/OpenFOAM/global/argList/argListHelp.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -454,7 +454,7 @@ void Foam::argList::printUsage(bool full) const
     printNotes();
 
     Info<< nl;
-    foamVersion::printBuildInfo(Info().stdStream(), true);
+    foamVersion::printBuildInfo(Info.stdStream(), true);
     Info<< endl;
 }
 
diff --git a/src/OpenFOAM/matrices/lduMatrix/solvers/GAMG/GAMGAgglomerations/GAMGAgglomeration/GAMGAgglomeration.C b/src/OpenFOAM/matrices/lduMatrix/solvers/GAMG/GAMGAgglomerations/GAMGAgglomeration/GAMGAgglomeration.C
index cf9b4aa1dc0..645cfaa152a 100644
--- a/src/OpenFOAM/matrices/lduMatrix/solvers/GAMG/GAMGAgglomerations/GAMGAgglomeration/GAMGAgglomeration.C
+++ b/src/OpenFOAM/matrices/lduMatrix/solvers/GAMG/GAMGAgglomerations/GAMGAgglomeration/GAMGAgglomeration.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -181,7 +181,7 @@ void Foam::GAMGAgglomeration::compactLevels(const label nCreatedLevels)
 
             scalar totProfile = returnReduce(profile, sumOp<scalar>());
 
-            int oldPrecision = Info().precision(4);
+            const int oldPrecision = Info.stream().precision(4);
 
             Info<< setw(8) << levelI
                 << setw(8) << totNprocs
@@ -200,7 +200,7 @@ void Foam::GAMGAgglomeration::compactLevels(const label nCreatedLevels)
                 << setw(12) << totProfile/totNprocs
                 << nl;
 
-            Info().precision(oldPrecision);
+            Info.stream().precision(oldPrecision);
         }
         Info<< endl;
     }
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
index 63e68240e77..d2f89372d0a 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -1684,7 +1684,7 @@ void Foam::snappyLayerDriver::calculateLayerThickness
     {
         const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
-        int oldPrecision = Info().precision();
+        const int oldPrecision = Info.stream().precision();
 
         // Find maximum length of a patch name, for a nicer output
         label maxPatchNameLen = 0;
@@ -3072,7 +3072,7 @@ void Foam::snappyLayerDriver::printLayerData
 {
     const polyBoundaryMesh& pbm = mesh.boundaryMesh();
 
-    int oldPrecision = Info().precision();
+    const int oldPrecision = Info.stream().precision();
 
     // Find maximum length of a patch name, for a nicer output
     label maxPatchNameLen = 0;
diff --git a/tutorials/incompressible/lumpedPointMotion/bridge/code/polynomial-motion.C b/tutorials/incompressible/lumpedPointMotion/bridge/code/polynomial-motion.C
index 88fcbf1b5b5..5c78be00cc8 100644
--- a/tutorials/incompressible/lumpedPointMotion/bridge/code/polynomial-motion.C
+++ b/tutorials/incompressible/lumpedPointMotion/bridge/code/polynomial-motion.C
@@ -474,7 +474,7 @@ int main(int argc, char *argv[])
         }
         else
         {
-            Info().precision(8);
+            Info.stream().precision(8);
         }
 
 
@@ -498,7 +498,7 @@ int main(int argc, char *argv[])
             else
             {
                 // Report position/angle
-                auto& os = Info();
+                auto& os = Info.stream();
 
                 os.writeEntry("time", currTime);
                 state.writeDict(os);
diff --git a/tutorials/incompressible/lumpedPointMotion/building/code/building-motion.C b/tutorials/incompressible/lumpedPointMotion/building/code/building-motion.C
index fcd94ccbb14..c1a940f8ccc 100644
--- a/tutorials/incompressible/lumpedPointMotion/building/code/building-motion.C
+++ b/tutorials/incompressible/lumpedPointMotion/building/code/building-motion.C
@@ -520,7 +520,7 @@ int main(int argc, char *argv[])
         }
         else
         {
-            Info().precision(8);
+            Info.stream().precision(8);
         }
 
 
@@ -544,7 +544,7 @@ int main(int argc, char *argv[])
             else
             {
                 // Report position/angle
-                auto& os = Info();
+                auto& os = Info.stream();
 
                 os.writeEntry("time", currTime);
                 state.writeDict(os);
-- 
GitLab