Commit e39949c7 authored by Mark Abraham's avatar Mark Abraham
Browse files

Removed dependency on commrec of mdrun setup

Changes no functionality.

Setup is now parameterized directly on MPI_COMM_WORLD, which we will
want later for letting library-based callers pass in an
MPI_Communicator. This permits commrec to be initialized later, once
the threads have been spawned for the thread-MPI ranks.

The initialization of multi-simulations moves from LegacyMdrunOptions
to SimulationContext, which is more appropriate for
ensemble-parallelism established directly by the user.

Before the decision about the duty of a rank, there is no difference
between MASTER(cr) and SIMMASTER(cr), so several calls to macros
taking a t_commrec pointer are replaced by booleans. Introduced
findIsSimulationMasterRank to compute that value. This eliminates
early use of t_commrec that has necessitated other hacks and
workarounds.

Removed redundant check for replica exchange when the number of multi
simulations is less than two, because gmx_multisim_t constructor
already prohibits that.

Resolves several TODO items and improves modularity, too.

Refs #2587, #2605, #3081

Change-Id: I48bd3b713bc181b5c1e4cbcd648706a9f00eab96
parent 671412d5
......@@ -54,13 +54,11 @@
#include "gromacs/commandline/pargs.h"
#include "gromacs/commandline/filenm.h"
#include "gromacs/commandline/pargs.h"
#include "gromacs/gmxlib/network.h"
#include "gromacs/mdlib/stophandler.h"
#include "gromacs/mdrunutility/logging.h"
#include "gromacs/mdrunutility/multisim.h"
#include "gromacs/mdrun/runner.h"
#include "gromacs/mdrunutility/handlerestart.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/utility/arraysize.h"
#include "gromacs/utility/fatalerror.h"
#include "gromacs/utility/smalloc.h"
......@@ -150,17 +148,7 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow &work)
strcpy(argv[argvIndex], mdArg.c_str());
}
auto mdModules = std::make_unique<MDModules>();
// pointer-to-t_commrec is the de facto handle type for communications record.
// Complete shared / borrowed ownership requires a reference to this stack variable
// (or pointer-to-pointer-to-t_commrec) since borrowing code may update the pointer.
// \todo Define the ownership and lifetime management semantics for a communication record, handle or value type.
// Note: this communications record initialization acts directly on
// MPI_COMM_WORLD and is incompatible with MPI environment sharing in
// gmxapi through 0.0.7, at least.
options_.cr = init_commrec();
auto mdModules = std::make_unique<MDModules>();
const char *desc[] = {"gmxapi placeholder text"};
if (options_.updateFromCommandLine(argc, argv.data(), desc) == 0)
......@@ -168,17 +156,32 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow &work)
return nullptr;
}
StartingBehavior startingBehavior = StartingBehavior::NewSimulation;
LogFilePtr logFileGuard = nullptr;
ArrayRef<const std::string> multiSimDirectoryNames =
opt2fnsIfOptionSet("-multidir", ssize(options_.filenames), options_.filenames.data());
// Set up the communicator, where possible (see docs for
// SimulationContext).
MPI_Comm communicator = GMX_LIB_MPI ? MPI_COMM_WORLD : MPI_COMM_NULL;
// The SimulationContext is necessary with gmxapi so that
// resources owned by the client code can have suitable
// lifetime. The gmx wrapper binary uses the same infrastructure,
// but the lifetime is now trivially that of the invocation of the
// wrapper binary.
SimulationContext simulationContext(communicator, multiSimDirectoryNames);
StartingBehavior startingBehavior = StartingBehavior::NewSimulation;
LogFilePtr logFileGuard = nullptr;
gmx_multisim_t *ms = simulationContext.multiSimulation_.get();
std::tie(startingBehavior,
logFileGuard) = handleRestart(options_.cr.get(),
options_.ms.get(),
logFileGuard) = handleRestart(findIsSimulationMasterRank(ms, communicator),
communicator,
ms,
options_.mdrunOptions.appendingBehavior,
ssize(options_.filenames),
options_.filenames.data());
auto simulationContext = createSimulationContext(std::move(options_.cr));
auto builder = MdrunnerBuilder(std::move(mdModules));
auto builder = MdrunnerBuilder(std::move(mdModules),
compat::not_null<SimulationContext*>(&simulationContext));
builder.addSimulationMethod(options_.mdrunOptions, options_.pforce, startingBehavior);
builder.addDomainDecomposition(options_.domdecOptions);
// \todo pass by value
......@@ -189,9 +192,6 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow &work)
builder.addUpdateTaskAssignment(options_.update_opt_choices[0]);
builder.addNeighborList(options_.nstlist_cmdline);
builder.addReplicaExchange(options_.replExParams);
// \todo take ownership of multisim resources (ms)
builder.addMultiSim(options_.ms.get());
// \todo Provide parallelism resources through SimulationContext.
// Need to establish run-time values from various inputs to provide a resource handle to Mdrunner
builder.addHardwareOptions(options_.hw_opt);
// \todo File names are parameters that should be managed modularly through further factoring.
......@@ -206,8 +206,7 @@ std::shared_ptr<Session> ContextImpl::launch(const Workflow &work)
launchedSession = createSession(shared_from_this(),
std::move(builder),
std::move(simulationContext),
std::move(logFileGuard),
options_.ms.get());
std::move(logFileGuard));
// Clean up argv once builder is no longer in use
for (auto && string : argv)
......
......@@ -46,8 +46,6 @@
#include "gmxapi/context.h"
#include "gmxapi/session.h"
struct gmx_multisim_t;
namespace gmx
{
class MdrunnerBuilder;
......@@ -73,8 +71,7 @@ namespace gmxapi
std::shared_ptr<Session> createSession(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr logFilehandle,
gmx_multisim_t * multiSim);
gmx::LogFilePtr logFilehandle);
} // end namespace gmxapi
......
......@@ -40,10 +40,8 @@
#include "gmxapi/session.h"
#include <memory>
#include "gromacs/gmxlib/network.h"
#include "gromacs/mdlib/sighandler.h"
#include "gromacs/mdrunutility/logging.h"
#include "gromacs/mdrunutility/multisim.h"
#include "gromacs/restraint/restraintpotential.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/basenetwork.h"
......@@ -65,14 +63,17 @@ namespace gmxapi
/*!
* \brief Provide RAII management of communications resource state.
*
* To acquire an MpiContextManager is to have assurance that the GROMACS MPI
* To acquire an MpiContextManager is to have assurance that any external MPI
* environment is ready to use. When the MpiContextManager is released or
* goes out of scope, the destructor finalizes the resources.
*
* \todo Figure out how to manage MPI versus tMPI.
* \todo gmx::init should take a subcommunicator rather than use MPI_COMM_WORLD
* Note that thread-MPI chooses the number of ranks and constructs its
* MPI communicator internally, so does not and is unlikely to ever
* participate here.
*
* \todo There is no resource for logging or reporting errors during initialization
* \todo Clarify relationship with gmx::SimulationContext.
* \todo Remove this class by managing the MPI context with mpi4py and so
* configuring the SimulationContext externally
*
* \ingroup gmxapi
*/
......@@ -82,27 +83,14 @@ class MpiContextManager
MpiContextManager()
{
gmx::init(nullptr, nullptr);
#ifdef GMX_MPI
#if GMX_MPI
#if GMX_THREAD_MPI
// With thread-MPI, gmx_mpi_initialized() is false until after
// spawnThreads in the middle of gmx::Mdrunner::mdrunner(), but
// without thread-MPI, MPI is initialized by the time gmx::init()
// returns. In other words, this is not an effective context manager
// for thread-MPI, but it should be effective for MPI.
// \todo Distinguish scope / lifetime for comm resources from implementation details.
// \todo Normalize scope / lifetime of comm resources.
#else
GMX_ASSERT(gmx_mpi_initialized(), "MPI should be initialized before reaching this point.");
#endif // GMX_THREAD_MPI
#endif // GMX_MPI
#endif // defined(GMX_MPI)
GMX_RELEASE_ASSERT(!GMX_LIB_MPI || gmx_mpi_initialized(), "MPI should be initialized before reaching this point.");
};
~MpiContextManager()
{
// This is safe to call. It is a no-op if thread-MPI, and if the
// constructor completed then MPI is initialized.
// This is always safe to call. It is a no-op if
// thread-MPI, and if the constructor completed then the
// MPI library is initialized with reference counting.
gmx::finalize();
}
......@@ -188,33 +176,27 @@ Status SessionImpl::run() noexcept
std::unique_ptr<SessionImpl> SessionImpl::create(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr logFilehandle,
gmx_multisim_t * multiSim)
gmx::LogFilePtr logFilehandle)
{
// We should be able to get a communicator (or subcommunicator) through the
// Context.
return std::make_unique<SessionImpl>(std::move(context),
std::move(runnerBuilder),
std::move(simulationContext),
std::move(logFilehandle),
multiSim);
std::move(logFilehandle));
}
SessionImpl::SessionImpl(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr fplog,
gmx_multisim_t * multiSim) :
gmx::LogFilePtr fplog) :
context_(std::move(context)),
mpiContextManager_(std::make_unique<MpiContextManager>()),
simulationContext_(std::move(simulationContext)),
logFilePtr_(std::move(fplog)),
multiSim_(multiSim)
logFilePtr_(std::move(fplog))
{
GMX_ASSERT(context_, "SessionImpl invariant implies valid ContextImpl handle.");
GMX_ASSERT(mpiContextManager_, "SessionImpl invariant implies valid MpiContextManager guard.");
GMX_ASSERT(simulationContext_.communicationRecord_, "SessionImpl invariant implies valid commrec.");
GMX_UNUSED_VALUE(multiSim_);
// \todo Session objects can have logic specialized for the runtime environment.
......@@ -222,7 +204,6 @@ SessionImpl::SessionImpl(std::shared_ptr<ContextImpl> context,
signalManager_ = std::make_unique<SignalManager>(stopHandlerBuilder.get());
GMX_ASSERT(signalManager_, "SessionImpl invariant includes a valid SignalManager.");
runnerBuilder.addCommunicationRecord(simulationContext_.communicationRecord_.get());
runnerBuilder.addStopHandlerBuilder(std::move(stopHandlerBuilder));
runner_ = std::make_unique<gmx::Mdrunner>(runnerBuilder.build());
GMX_ASSERT(runner_, "SessionImpl invariant implies valid Mdrunner handle.");
......@@ -235,14 +216,12 @@ SessionImpl::SessionImpl(std::shared_ptr<ContextImpl> context,
std::shared_ptr<Session> createSession(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr logFilehandle,
gmx_multisim_t * multiSim)
gmx::LogFilePtr logFilehandle)
{
auto newSession = SessionImpl::create(std::move(context),
std::move(runnerBuilder),
std::move(simulationContext),
std::move(logFilehandle),
multiSim);
std::move(logFilehandle));
auto launchedSession = std::make_shared<Session>(std::move(newSession));
return launchedSession;
}
......
......@@ -114,7 +114,6 @@ class SessionImpl
* \param runnerBuilder MD simulation builder to take ownership of.
* \param simulationContext Take ownership of the simulation resources.
* \param logFilehandle Take ownership of filehandle for MD logging
* \param multiSim Take ownership of resources for Mdrunner multi-sim.
*
* \todo Log file management will be updated soon.
*
......@@ -123,8 +122,7 @@ class SessionImpl
static std::unique_ptr<SessionImpl> create(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr logFilehandle,
gmx_multisim_t * multiSim);
gmx::LogFilePtr logFilehandle);
/*!
* \brief Add a restraint to the simulation.
......@@ -184,14 +182,12 @@ class SessionImpl
* \param runnerBuilder ownership of the MdrunnerBuilder object.
* \param simulationContext take ownership of a SimulationContext
* \param logFilehandle Take ownership of filehandle for MD logging
* \param multiSim Take ownership of resources for Mdrunner multi-sim.
*
*/
SessionImpl(std::shared_ptr<ContextImpl> context,
gmx::MdrunnerBuilder &&runnerBuilder,
gmx::SimulationContext &&simulationContext,
gmx::LogFilePtr logFilehandle,
gmx_multisim_t * multiSim);
gmx::LogFilePtr logFilehandle);
private:
/*!
......@@ -234,17 +230,6 @@ class SessionImpl
*/
gmx::LogFilePtr logFilePtr_;
/*!
* \brief MultiSim resources for Mdrunner instance.
*
* May be null for no multi-simulation management at the Mdrunner level.
*
* \todo Lifetime of the multi-simulation handle is currently
* managed by LegacyMdrunOptions, but in the long term,
* session needs to manage it.
*/
gmx_multisim_t* multiSim_;
/*!
* \brief Own and manager the signalling pathways for the current session.
*
......
......@@ -115,7 +115,7 @@ class ElectricFieldTest : public ::testing::Test
std::vector<real> chargeA { 1 };
md.homenr = ssize(chargeA);
md.chargeA = chargeA.data();
CommrecHandle cr = init_commrec();
CommrecHandle cr = init_commrec(MPI_COMM_WORLD, nullptr);
matrix boxDummy = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} };
ForceProviderInput forceProviderInput({}, md, 0.0, boxDummy, *cr);
......
......@@ -46,6 +46,7 @@
#include <cstring>
#include "gromacs/commandline/filenm.h"
#include "gromacs/mdrunutility/multisim.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/utility/basenetwork.h"
#include "gromacs/utility/cstringutil.h"
......@@ -59,26 +60,8 @@
/* The source code in this file should be thread-safe.
Please keep it that way. */
void gmx_fill_commrec_from_mpi(t_commrec *cr)
{
#if !GMX_MPI
gmx_call("gmx_fill_commrec_from_mpi");
GMX_UNUSED_VALUE(cr);
#else
if (!gmx_mpi_initialized())
{
gmx_comm("MPI has not been initialized properly");
}
cr->nnodes = gmx_node_num();
cr->nodeid = gmx_node_rank();
cr->sim_nodeid = cr->nodeid;
cr->mpi_comm_mysim = MPI_COMM_WORLD;
cr->mpi_comm_mygroup = MPI_COMM_WORLD;
#endif
}
CommrecHandle init_commrec()
CommrecHandle init_commrec(MPI_Comm communicator,
const gmx_multisim_t *ms)
{
CommrecHandle handle;
t_commrec *cr;
......@@ -86,16 +69,38 @@ CommrecHandle init_commrec()
snew(cr, 1);
handle.reset(cr);
#if GMX_LIB_MPI
gmx_fill_commrec_from_mpi(cr);
int rankInCommunicator, sizeOfCommunicator;
#if GMX_MPI
# if GMX_LIB_MPI
GMX_RELEASE_ASSERT(gmx_mpi_initialized(), "Must have initialized MPI before building commrec");
# endif
MPI_Comm_rank(communicator, &rankInCommunicator);
MPI_Comm_size(communicator, &sizeOfCommunicator);
#else
cr->mpi_comm_mysim = MPI_COMM_NULL;
cr->mpi_comm_mygroup = MPI_COMM_NULL;
cr->nnodes = 1;
cr->sim_nodeid = 0;
cr->nodeid = cr->sim_nodeid;
GMX_UNUSED_VALUE(communicator);
rankInCommunicator = 0;
sizeOfCommunicator = 1;
#endif
if (ms != nullptr)
{
#if GMX_MPI
cr->nnodes = sizeOfCommunicator / ms->nsim;
MPI_Comm_split(communicator, ms->sim, rankInCommunicator, &cr->mpi_comm_mysim);
cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
MPI_Comm_rank(cr->mpi_comm_mysim, &cr->sim_nodeid);
MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
#endif
}
else
{
cr->nnodes = sizeOfCommunicator;
cr->nodeid = rankInCommunicator;
cr->sim_nodeid = cr->nodeid;
cr->mpi_comm_mysim = communicator;
cr->mpi_comm_mygroup = communicator;
}
// TODO cr->duty should not be initialized here
cr->duty = (DUTY_PP | DUTY_PME);
......@@ -148,30 +153,6 @@ void done_commrec(t_commrec *cr)
sfree(cr);
}
t_commrec *reinitialize_commrec_for_this_thread(const t_commrec *cro)
{
#if GMX_THREAD_MPI
t_commrec *cr;
/* make a thread-specific commrec */
snew(cr, 1);
/* now copy the whole thing, so settings like the number of PME nodes
get propagated. */
*cr = *cro;
/* and we start setting our own thread-specific values for things */
gmx_fill_commrec_from_mpi(cr);
// TODO cr->duty should not be initialized here
cr->duty = (DUTY_PP | DUTY_PME);
return cr;
#else
GMX_UNUSED_VALUE(cro);
return nullptr;
#endif
}
void gmx_setup_nodecomm(FILE gmx_unused *fplog, t_commrec *cr)
{
gmx_nodecomm_t *nc;
......
......@@ -48,6 +48,7 @@
#include "gromacs/utility/stringutil.h"
#include "gromacs/utility/unique_cptr.h"
struct gmx_multisim_t;
struct t_commrec;
struct t_filenm;
......@@ -58,7 +59,8 @@ void done_commrec(t_commrec *cr);
using CommrecHandle = gmx::unique_cptr<t_commrec, done_commrec>;
//! Allocate, initialize and return the commrec.
CommrecHandle init_commrec();
CommrecHandle init_commrec(MPI_Comm communicator,
const gmx_multisim_t *ms);
struct t_commrec *reinitialize_commrec_for_this_thread(const t_commrec *cro);
......
......@@ -45,7 +45,7 @@ class MDLogger;
}
/* Print information about the detected hardware to fplog (if != NULL)
* and to stderr the master rank.
* and to stderr on the master rank of the master simulation.
*/
void gmx_print_detected_hardware(FILE *fplog,
bool warnToStdErr,
......
......@@ -49,15 +49,9 @@
#include "legacymdrunoptions.h"
#include "config.h"
#include <cstring>
#include "gromacs/gmxlib/network.h"
#include "gromacs/math/functions.h"
#include "gromacs/mdrunutility/handlerestart.h"
#include "gromacs/mdrunutility/multisim.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/utility/arraysize.h"
#include "gromacs/utility/fatalerror.h"
......@@ -138,29 +132,6 @@ int LegacyMdrunOptions::updateFromCommandLine(int argc, char **argv, ArrayRef<co
hw_opt.threadAffinity = static_cast<ThreadAffinity>(nenum(thread_aff_opt_choices));
// now check for a multi-simulation
ArrayRef<const std::string> multidir = opt2fnsIfOptionSet("-multidir",
ssize(filenames),
filenames.data());
if (!multidir.empty())
{
ms = std::make_unique<gmx_multisim_t>(MPI_COMM_WORLD, multidir);
}
/* Prepare the intra-simulation communication */
// TODO consolidate this with init_commrec, after changing the
// relative ordering of init_commrec and ms initialization.
if (ms != nullptr)
{
#if GMX_MPI
cr->nnodes = cr->nnodes / ms->nsim;
MPI_Comm_split(MPI_COMM_WORLD, ms->sim, cr->sim_nodeid, &cr->mpi_comm_mysim);
cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
MPI_Comm_rank(cr->mpi_comm_mysim, &cr->sim_nodeid);
MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
#endif
}
if (!opt2parg_bSet("-append", asize(pa), pa))
{
mdrunOptions.appendingBehavior = AppendingBehavior::Auto;
......
......@@ -52,15 +52,11 @@
#include "gromacs/commandline/filenm.h"
#include "gromacs/commandline/pargs.h"
#include "gromacs/domdec/options.h"
#include "gromacs/gmxlib/network.h"
#include "gromacs/hardware/hw_info.h"
#include "gromacs/mdrunutility/multisim.h"
#include "gromacs/mdtypes/mdrunoptions.h"
#include "replicaexchange.h"
struct gmx_multisim_t;
namespace gmx
{
......@@ -76,8 +72,8 @@ namespace gmx
* that all of these declarations and defaults are local to the
* modules.
*
* \todo Contextual aspects, such as working directory, MPI
* environment, and environment variable handling are more properly
* \todo Contextual aspects, such as working directory
* and environment variable handling are more properly
* the role of SimulationContext, and should be moved there */
class LegacyMdrunOptions
{
......@@ -272,11 +268,6 @@ class LegacyMdrunOptions
};
/*! \} */
//! Handle to communication object.
CommrecHandle cr = init_commrec();
//! Multi-simulation object.
std::unique_ptr<gmx_multisim_t> ms;
//! Parses the command-line input and prepares to start mdrun.
int updateFromCommandLine(int argc, char **argv, ArrayRef<const char *> desc);
......
......@@ -230,9 +230,6 @@ Mdrunner Mdrunner::cloneOnSpawnedThread() const
newRunner.restraintManager_ = std::make_unique<RestraintManager>(*restraintManager_);
}
// Copy original cr pointer before master thread can pass the thread barrier
newRunner.cr = reinitialize_commrec_for_this_thread(cr);
// Copy members of master runner.
// \todo Replace with builder when Simulation context and/or runner phases are better defined.
// Ref https://redmine.gromacs.org/issues/2587 and https://redmine.gromacs.org/issues/2375
......@@ -250,14 +247,15 @@ Mdrunner Mdrunner::cloneOnSpawnedThread() const
newRunner.nstlist_cmdline = nstlist_cmdline;
newRunner.replExParams = replExParams;
newRunner.pforce = pforce;
// Give the spawned thread the newly created valid communicator
// for the simulation.
newRunner.communicator = MPI_COMM_WORLD;
newRunner.ms = ms;
newRunner.startingBehavior = startingBehavior;
newRunner.stopHandlerBuilder_ = std::make_unique<StopHandlerBuilder>(*stopHandlerBuilder_);
threadMpiMdrunnerAccessBarrier();
GMX_RELEASE_ASSERT(!MASTER(newRunner.cr), "cloneOnSpawnedThread should only be called on spawned threads");
return newRunner;
}
......@@ -282,21 +280,8 @@ static void mdrunner_start_fn(const void *arg)
}
/*! \brief Start thread-MPI threads.
*
* Called by mdrunner() to start a specific number of threads
* (including the main thread) for thread-parallel runs. This in turn
* calls mdrunner() for each thread. All options are the same as for
* mdrunner(). */
t_commrec *Mdrunner::spawnThreads(int numThreadsToLaunch) const
void Mdrunner::spawnThreads(int numThreadsToLaunch)
{
/* first check whether we even need to start tMPI */
if (numThreadsToLaunch < 2)
{
return cr;
}
#if GMX_THREAD_MPI
/* now spawn new threads that start mdrunner_start_fn(), while
the main thread returns. Thread affinity is handled later. */
......@@ -306,12 +291,14 @@ t_commrec *Mdrunner::spawnThreads(int numThreadsToLaunch) const
GMX_THROW(gmx::InternalError("Failed to spawn thread-MPI threads"));
}
// Give the master thread the newly created valid communicator for
// the simulation.
communicator = MPI_COMM_WORLD;
threadMpiMdrunnerAccessBarrier();
#else
GMX_UNUSED_VALUE(numThreadsToLaunch);
GMX_UNUSED_VALUE(mdrunner_start_fn);
#endif
return reinitialize_commrec_for_this_thread(cr);
}
} // namespace gmx
......@@ -449,14 +436,15 @@ static bool gpuAccelerationOfNonbondedIsUseful(const MDLogger &mdlog,
}
//! Initializes the logger for mdrun.
static gmx::LoggerOwner buildLogger(FILE *fplog, const t_commrec *cr)
static gmx::LoggerOwner buildLogger(FILE *fplog,
const bool isSimulationMasterRank)
{
gmx::LoggerBuilder builder;
if (fplog != nullptr)
{
builder.addTargetFile(gmx::MDLogger::LogLevel::Info, fplog);
}
if (cr == nullptr || SIMMASTER(cr))
if (isSimulationMasterRank)
{
builder.addTargetStream(gmx::MDLogger::LogLevel::Warning,
&gmx::TextOutputFile::standardError());
......@@ -661,10 +649,7 @@ int Mdrunner::mdrunner()
auto updateTarget = findTaskTarget(update_opt);
PmeRunMode pmeRunMode = PmeRunMode::None;
// Here we assume that SIMMASTER(cr) does not change even after the
// threads are started.
FILE *fplog = nullptr;
FILE *fplog = nullptr;
// If we are appending, we don't write log output because we need
// to check that the old log file matches what the checkpoint file
// expects. Otherwise, we should start to write log output now if
...