diff --git a/src/api/cpp/CMakeLists.txt b/src/api/cpp/CMakeLists.txt index f7813f200..31124f65d 100644 --- a/src/api/cpp/CMakeLists.txt +++ b/src/api/cpp/CMakeLists.txt @@ -17,10 +17,16 @@ configure_file(gmxapi/session.h gmxapi) configure_file(gmxapi/status.h gmxapi) configure_file(gmxapi/system.h gmxapi) configure_file(gmxapi/version.in.h gmxapi/version.h) + # headers for API development and extension + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gmxapi/md) #configure_file(gmxapi/md/runnerstate.h gmxapi/md) configure_file(gmxapi/md/mdmodule.h gmxapi/md) +configure_file(gmxapi/md/mdsignals.h gmxapi/md) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gmxapi/context) +configure_file(gmxapi/context/outputstream.h gmxapi/context) # Add to install target: copy the public API headers from the source directory # into the gmxapi header subdirectory. @@ -64,11 +70,14 @@ target_include_directories(gmxapi PRIVATE # but listing all source files helps some code introspection tools work better. target_sources( gmxapi PRIVATE + boolean.cpp context.cpp gmxapi.cpp md-impl.h md.cpp mdmodule.cpp + mdsignals.cpp + outputstream.cpp session-impl.h session.cpp status.cpp diff --git a/src/api/cpp/boolean.cpp b/src/api/cpp/boolean.cpp new file mode 100644 index 000000000..d5ca0a7de --- /dev/null +++ b/src/api/cpp/boolean.cpp @@ -0,0 +1,5 @@ +// +// Created by Eric Irrgang on 5/21/18. +// + +#include "gmxapi/workflow/boolean.h" diff --git a/src/api/cpp/gmxapi/context/outputstream.h b/src/api/cpp/gmxapi/context/outputstream.h new file mode 100644 index 000000000..d53c1dd30 --- /dev/null +++ b/src/api/cpp/gmxapi/context/outputstream.h @@ -0,0 +1,52 @@ +// +// Created by Eric Irrgang on 5/21/18. +// + +#ifndef GROMACS_OUTPUTSTREAM_H +#define GROMACS_OUTPUTSTREAM_H + +#include +#include + +namespace gmxapi { +namespace context { + +class OutputStream +{ + public: + ~OutputStream(); + + /*! + * \brief Set data for a registered output stream. + * + * \param outputName Registered name of the output port + * \param data data to set with the registered output handler. + * + * We should not use a template here to handle the different data types because the template might be expressed + * with different munged symbol names by different compilers. But we want this interface to be extensible, so + * we need to consider how to allow flexible types. We could wrap all data in a gmxapi::Data wrapper or something, + * but that makes calling the set() method more cumbersome in the client code. + * + * What we could do, though, is to provide a template function as a helper that is compiled in the client code + * and just makes it easier to make the correct call here. Then a gmxapi::Data wrapper wouldn't be cumbersome. + */ + void set(std::string outputName, bool data); + //void set(std::string outputName, someOtherType + + void set(const char* outputName, bool data) + { + this->set(std::string(outputName), data); + } + + private: + // Private implementation class + class Impl; + // opaque pointer to implementation. + std::unique_ptr impl_; +}; + +} // end namespace gmxapi::context +} //end namespace gmxapi + + +#endif //GROMACS_OUTPUTSTREAM_H diff --git a/src/api/cpp/gmxapi/md/mdsignals.h b/src/api/cpp/gmxapi/md/mdsignals.h new file mode 100644 index 000000000..aa6945c4d --- /dev/null +++ b/src/api/cpp/gmxapi/md/mdsignals.h @@ -0,0 +1,59 @@ +// +// Created by Eric Irrgang on 5/18/18. +// + +/* WARNING + * This whole file is not intended to make it into a public release and is not part of the gmxapi API. It is for + * prototyping only. Please don't let it slip into a release without serious design considerations. + */ + +#ifndef GMXAPI_MDSIGNALS_H +#define GMXAPI_MDSIGNALS_H + +#include + +namespace gmxapi { + +namespace md +{ + +enum class signals { + STOP +}; + +} // end namespace md + + +class Session; // defined in gmxapi/session.h + +/*! + * \brief Proxy for signalling function objects. + * + * Objects of this type are simple callables that issue a specific signal. + */ +class Signal +{ + public: + class SignalImpl; + explicit Signal(std::unique_ptr&& signal); + Signal(Signal&& signal); + Signal& operator=(Signal&& signal); + ~Signal(); + + void operator()(); + + private: + std::unique_ptr impl_; +}; + +/*! + * \brief Get a function object that issues a signal to the currently active MD runner. + * + * \param session pointer to the active Session. + * \return Callable function object handle + */ +Signal getMdrunnerSignal(Session* session, md::signals signal); + +} // end namespace md + +#endif //GMXAPI_MDSIGNALS_H diff --git a/src/api/cpp/gmxapi/session.h b/src/api/cpp/gmxapi/session.h index ffc0dec15..495ad199a 100644 --- a/src/api/cpp/gmxapi/session.h +++ b/src/api/cpp/gmxapi/session.h @@ -107,6 +107,17 @@ class Session bool isOpen() const noexcept; + /*! \internal + * \brief Get a non-owning handle to the implementation object. + * + * Get a raw pointer to the implementation object. The pointer is valid only during the lifetime of the Session, + * so retain a shared pointer to this Session object or only hold the pointer for the duration of a code block + * guaranteed to exist entirely within the lifetime of a Session object. + * + * \return opaque pointer used by gmxapi implementation and extension code. + */ + SessionImpl* getRaw() const noexcept; + private: friend Status setSessionRestraint(Session* session, diff --git a/src/api/cpp/gmxapi/workflow/boolean.h b/src/api/cpp/gmxapi/workflow/boolean.h new file mode 100644 index 000000000..b6651df3d --- /dev/null +++ b/src/api/cpp/gmxapi/workflow/boolean.h @@ -0,0 +1,19 @@ +// +// Created by Eric Irrgang on 5/21/18. +// + +#ifndef GROMACS_BOOLEAN_H +#define GROMACS_BOOLEAN_H + +/*! + * \brief Workflow element for Boolean operations. + * + * Produce Boolean (true or false) output for logical operations performed on Boolean inputs. + */ +class Boolean +{ + +}; + + +#endif //GROMACS_BOOLEAN_H diff --git a/src/api/cpp/mdsignals.cpp b/src/api/cpp/mdsignals.cpp new file mode 100644 index 000000000..b11974650 --- /dev/null +++ b/src/api/cpp/mdsignals.cpp @@ -0,0 +1,104 @@ +// +// Created by Eric Irrgang on 5/18/18. +// + +#include "gmxapi/md/mdsignals.h" + +#include + +#include "gromacs/compat/make_unique.h" + +#include "gromacs/mdlib/simulationsignal.h" +#include "programs/mdrun/runner.h" + +#include "gmxapi/session.h" + +#include "session-impl.h" + +namespace gmxapi { + +class Signal::SignalImpl +{ + public: + virtual void call() = 0; + + +}; + +Signal::Signal(std::unique_ptr&& impl) : + impl_{std::move(impl)} +{ +} + +Signal::~Signal() = default; + +void Signal::operator()() +{ + impl_->call(); +} + +Signal::Signal(Signal &&signal) = default; + +Signal &Signal::operator=(Signal &&signal) = default; + +class StopSignal : public Signal::SignalImpl +{ + public: + explicit StopSignal(gmx::Mdrunner* runner) : runner_{runner} {}; + + StopSignal(gmx::Mdrunner* runner, unsigned int numParticipants) : StopSignal(runner) + { + StopSignal::numParticipants_.store(numParticipants); + } + + void call() override + { + unsigned int n{++StopSignal::numCalls_}; + if (n >= StopSignal::numParticipants_.load()) + { + auto signals = runner_->signals(); + // sig > 0 stops at next NS step. sig < 0 stops at next step. + signals->at(eglsSTOPCOND).sig = -1; + } + } + + private: + gmx::Mdrunner* runner_; + + // Number of participants in this signal + static std::atomic numParticipants_; + + // Number of times the signal has been called. + static std::atomic numCalls_; +}; + +std::atomic StopSignal::numParticipants_{0}; +std::atomic StopSignal::numCalls_{0}; + +Signal getMdrunnerSignal(Session* session, md::signals signal) +{ +//// while there is only one choice... +// if (signal == md::signals::STOP) +// { + assert(signal == md::signals::STOP); + assert(session); + + auto impl = session->getRaw(); + assert(impl); + + auto runner = impl->getRunner(); + assert(runner); + + // std::unique_ptr signalImpl = gmx::compat::make_unique(runner); + std::unique_ptr signalImpl = gmx::compat::make_unique(runner, impl->numRestraints); + + Signal functor{std::move(signalImpl)}; + + return functor; +// } +// else +// { +// } +} + +} // end namespace gmxapi diff --git a/src/api/cpp/outputstream.cpp b/src/api/cpp/outputstream.cpp new file mode 100644 index 000000000..7a7e1296c --- /dev/null +++ b/src/api/cpp/outputstream.cpp @@ -0,0 +1,20 @@ +// +// Created by Eric Irrgang on 5/21/18. +// + +#include "gmxapi/context/outputstream.h" + +namespace gmxapi { +namespace context { + +class OutputStream::Impl { +}; + +void OutputStream::set(std::string outputName, + bool data) +{} + +OutputStream::~OutputStream() = default; + +} // end namespace gmxapi::context +} // end namespace gmxapi diff --git a/src/api/cpp/session-impl.h b/src/api/cpp/session-impl.h index 33b2afecd..2d502fc79 100644 --- a/src/api/cpp/session-impl.h +++ b/src/api/cpp/session-impl.h @@ -16,6 +16,7 @@ namespace gmxapi // Forward declaration class MpiContextManager; // Locally defined in session.cpp +class ContextImpl; // locally defined in context.cpp /*! * \brief Implementation class for executing sessions. @@ -71,6 +72,15 @@ class SessionImpl std::unique_ptr runner); Status setRestraint(std::shared_ptr module); + + /*! \internal + * \brief API implementation function to retrieve the current runner. + * + * \return non-owning pointer to the current runner or nullptr if none. + */ + gmx::Mdrunner* getRunner(); + + int numRestraints{0}; private: /*! * \brief Private constructor for use by create() diff --git a/src/api/cpp/session.cpp b/src/api/cpp/session.cpp index 1a4c8d154..76e105799 100644 --- a/src/api/cpp/session.cpp +++ b/src/api/cpp/session.cpp @@ -9,6 +9,7 @@ #include "gromacs/utility/init.h" #include "gmxapi/md/mdmodule.h" #include "gromacs/compat/make_unique.h" +#include "gromacs/restraint/restraintpotential.h" #include "gmxapi/context.h" #include "gmxapi/status.h" @@ -139,6 +140,16 @@ Status SessionImpl::setRestraint(std::shared_ptr module) return status; } +gmx::Mdrunner *SessionImpl::getRunner() +{ + gmx::Mdrunner * runner{nullptr}; + if (runner_) + { + runner = runner_.get(); + } + return runner; +} + Session::Session(std::unique_ptr&& impl) noexcept : impl_{std::move(impl)} { @@ -201,11 +212,28 @@ bool Session::isOpen() const noexcept Status setSessionRestraint(Session *session, std::shared_ptr module) { + auto status = gmxapi::Status(false); - auto status = session->impl_->setRestraint(std::move(module)); + if (session != nullptr && module != nullptr) + { + auto restraint = module->getRestraint(); + if (restraint != nullptr) + { + restraint->bindSession(session); + session->impl_->numRestraints += 1; + } + + assert(session->impl_); + auto status = session->impl_->setRestraint(std::move(module)); + } return status; } +SessionImpl *Session::getRaw() const noexcept +{ + return impl_.get(); +} + std::shared_ptr launchSession(Context* context, const Workflow& work) noexcept { auto session = context->launch(work); diff --git a/src/api/cpp/system.cpp b/src/api/cpp/system.cpp index 35fa3144b..b2cfd023a 100644 --- a/src/api/cpp/system.cpp +++ b/src/api/cpp/system.cpp @@ -130,6 +130,7 @@ std::shared_ptr System::Impl::launch(std::shared_ptr context) if (context != nullptr) { session = context->launch(*workflow_); + assert(session); for (auto&& module : spec_->getModules()) { diff --git a/src/gromacs/mdlib/integrator.h b/src/gromacs/mdlib/integrator.h index e8fdaa42d..f7e265e3c 100644 --- a/src/gromacs/mdlib/integrator.h +++ b/src/gromacs/mdlib/integrator.h @@ -70,6 +70,8 @@ namespace gmx class IMDOutputProvider; class MDLogger; +namespace md { class Context; } + /*! \brief Integrator algorithm implementation. * * \param[in] fplog Log file for output @@ -120,7 +122,8 @@ typedef double integrator_t (FILE *fplog, t_commrec *cr, const gmx::MDLogger &md real cpt_period, real max_hours, int imdport, unsigned long Flags, - gmx_walltime_accounting_t walltime_accounting); + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context& context); } // namespace gmx diff --git a/src/gromacs/mdlib/minimize.cpp b/src/gromacs/mdlib/minimize.cpp index 04e6b7039..0eec47dfc 100644 --- a/src/gromacs/mdlib/minimize.cpp +++ b/src/gromacs/mdlib/minimize.cpp @@ -99,6 +99,7 @@ #include "gromacs/utility/fatalerror.h" #include "gromacs/utility/logger.h" #include "gromacs/utility/smalloc.h" +#include "programs/mdrun/context.h" //! Utility structure for manipulating states during EM typedef struct { @@ -1010,8 +1011,10 @@ double do_cg(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog, real gmx_unused cpt_period, real gmx_unused max_hours, int imdport, unsigned long gmx_unused Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context& context) { + (void) context; const char *CG = "Polak-Ribiere Conjugate Gradients"; gmx_localtop_t *top; @@ -1643,27 +1646,37 @@ double do_cg(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog, unsigned long Flags, gmx_walltime_accounting_t walltime_accounting) */ -double do_lbfgs(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog, - int nfile, const t_filenm fnm[], - const gmx_output_env_t gmx_unused *oenv, gmx_bool bVerbose, +double do_lbfgs(FILE *fplog, + t_commrec *cr, + const gmx::MDLogger gmx_unused &mdlog, + int nfile, + const t_filenm fnm[], + const gmx_output_env_t gmx_unused *oenv, + gmx_bool bVerbose, int gmx_unused nstglobalcomm, - gmx_vsite_t *vsite, gmx_constr_t constr, + gmx_vsite_t *vsite, + gmx_constr_t constr, int gmx_unused stepout, gmx::IMDOutputProvider *outputProvider, t_inputrec *inputrec, - gmx_mtop_t *top_global, t_fcdata *fcd, + gmx_mtop_t *top_global, + t_fcdata *fcd, t_state *state_global, ObservablesHistory *observablesHistory, t_mdatoms *mdatoms, - t_nrnb *nrnb, gmx_wallcycle_t wcycle, + t_nrnb *nrnb, + gmx_wallcycle_t wcycle, t_forcerec *fr, const ReplicaExchangeParameters gmx_unused &replExParams, gmx_membed_t gmx_unused *membed, - real gmx_unused cpt_period, real gmx_unused max_hours, + real gmx_unused cpt_period, + real gmx_unused max_hours, int imdport, unsigned long gmx_unused Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context &context) { + (void) context; static const char *LBFGS = "Low-Memory BFGS Minimizer"; em_state_t ems; gmx_localtop_t *top; @@ -2415,27 +2428,37 @@ double do_lbfgs(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo unsigned long Flags, gmx_walltime_accounting_t walltime_accounting) */ -double do_steep(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog, - int nfile, const t_filenm fnm[], - const gmx_output_env_t gmx_unused *oenv, gmx_bool bVerbose, +double do_steep(FILE *fplog, + t_commrec *cr, + const gmx::MDLogger gmx_unused &mdlog, + int nfile, + const t_filenm fnm[], + const gmx_output_env_t gmx_unused *oenv, + gmx_bool bVerbose, int gmx_unused nstglobalcomm, - gmx_vsite_t *vsite, gmx_constr_t constr, + gmx_vsite_t *vsite, + gmx_constr_t constr, int gmx_unused stepout, gmx::IMDOutputProvider *outputProvider, t_inputrec *inputrec, - gmx_mtop_t *top_global, t_fcdata *fcd, + gmx_mtop_t *top_global, + t_fcdata *fcd, t_state *state_global, ObservablesHistory *observablesHistory, t_mdatoms *mdatoms, - t_nrnb *nrnb, gmx_wallcycle_t wcycle, + t_nrnb *nrnb, + gmx_wallcycle_t wcycle, t_forcerec *fr, const ReplicaExchangeParameters gmx_unused &replExParams, gmx_membed_t gmx_unused *membed, - real gmx_unused cpt_period, real gmx_unused max_hours, + real gmx_unused cpt_period, + real gmx_unused max_hours, int imdport, unsigned long gmx_unused Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context &context) { + (void) context; const char *SD = "Steepest Descents"; gmx_localtop_t *top; gmx_enerdata_t *enerd; @@ -2685,27 +2708,37 @@ double do_steep(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlo unsigned long Flags, gmx_walltime_accounting_t walltime_accounting) */ -double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, - int nfile, const t_filenm fnm[], - const gmx_output_env_t gmx_unused *oenv, gmx_bool bVerbose, +double do_nm(FILE *fplog, + t_commrec *cr, + const gmx::MDLogger &mdlog, + int nfile, + const t_filenm fnm[], + const gmx_output_env_t gmx_unused *oenv, + gmx_bool bVerbose, int gmx_unused nstglobalcomm, - gmx_vsite_t *vsite, gmx_constr_t constr, + gmx_vsite_t *vsite, + gmx_constr_t constr, int gmx_unused stepout, gmx::IMDOutputProvider *outputProvider, t_inputrec *inputrec, - gmx_mtop_t *top_global, t_fcdata *fcd, + gmx_mtop_t *top_global, + t_fcdata *fcd, t_state *state_global, ObservablesHistory gmx_unused *observablesHistory, t_mdatoms *mdatoms, - t_nrnb *nrnb, gmx_wallcycle_t wcycle, + t_nrnb *nrnb, + gmx_wallcycle_t wcycle, t_forcerec *fr, const ReplicaExchangeParameters gmx_unused &replExParams, gmx_membed_t gmx_unused *membed, - real gmx_unused cpt_period, real gmx_unused max_hours, + real gmx_unused cpt_period, + real gmx_unused max_hours, int imdport, unsigned long gmx_unused Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context &context) { + (void) context; const char *NM = "Normal Mode Analysis"; gmx_mdoutf_t outf; int nnodes, node; diff --git a/src/gromacs/mdlib/tpi.cpp b/src/gromacs/mdlib/tpi.cpp index 0e99f3f59..1928e4070 100644 --- a/src/gromacs/mdlib/tpi.cpp +++ b/src/gromacs/mdlib/tpi.cpp @@ -147,27 +147,37 @@ namespace gmx unsigned long Flags, gmx_walltime_accounting_t walltime_accounting) */ -double do_tpi(FILE *fplog, t_commrec *cr, const gmx::MDLogger gmx_unused &mdlog, - int nfile, const t_filenm fnm[], - const gmx_output_env_t *oenv, gmx_bool bVerbose, +double do_tpi(FILE *fplog, + t_commrec *cr, + const gmx::MDLogger gmx_unused &mdlog, + int nfile, + const t_filenm fnm[], + const gmx_output_env_t *oenv, + gmx_bool bVerbose, int gmx_unused nstglobalcomm, - gmx_vsite_t gmx_unused *vsite, gmx_constr_t gmx_unused constr, + gmx_vsite_t gmx_unused *vsite, + gmx_constr_t gmx_unused constr, int gmx_unused stepout, gmx::IMDOutputProvider *outputProvider, t_inputrec *inputrec, - gmx_mtop_t *top_global, t_fcdata *fcd, + gmx_mtop_t *top_global, + t_fcdata *fcd, t_state *state_global, ObservablesHistory gmx_unused *observablesHistory, t_mdatoms *mdatoms, - t_nrnb *nrnb, gmx_wallcycle_t wcycle, + t_nrnb *nrnb, + gmx_wallcycle_t wcycle, t_forcerec *fr, const ReplicaExchangeParameters gmx_unused &replExParams, gmx_membed_t gmx_unused *membed, - real gmx_unused cpt_period, real gmx_unused max_hours, + real gmx_unused cpt_period, + real gmx_unused max_hours, int gmx_unused imdport, unsigned long gmx_unused Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context &context) { + (void) context; gmx_localtop_t *top; gmx_groups_t *groups; gmx_enerdata_t *enerd; diff --git a/src/gromacs/restraint/manager.cpp b/src/gromacs/restraint/manager.cpp index 8212a3f9d..5f8577905 100644 --- a/src/gromacs/restraint/manager.cpp +++ b/src/gromacs/restraint/manager.cpp @@ -307,8 +307,8 @@ void Manager::add(std::shared_ptr puller, std::string name) impl_->addLegacy(std::move(puller), std::move(name)); } -void Manager::addSpec(std::shared_ptr puller, - std::string name) +void Manager::addToSpec(std::shared_ptr puller, + std::string name) { assert(impl_ != nullptr); impl_->add(std::move(puller), name); diff --git a/src/gromacs/restraint/manager.h b/src/gromacs/restraint/manager.h index e8e5a5199..1d9e28937 100644 --- a/src/gromacs/restraint/manager.h +++ b/src/gromacs/restraint/manager.h @@ -82,13 +82,14 @@ class Manager final /*! \brief Obtain the ability to create a restraint MDModule * - * First iteration allows one restraint. + * Though the name is reminiscent of the evolving idea of a work specification, the + * Spec here is just a list of restraint modules. * * \param puller shared ownership of a restraint potential interface. * \param name key by which to reference the restraint. */ - void addSpec(std::shared_ptr puller, - std::string name); + void addToSpec(std::shared_ptr puller, + std::string name); std::vector> getSpec() const; diff --git a/src/gromacs/restraint/restraintpotential.h b/src/gromacs/restraint/restraintpotential.h index 6bb90e863..690adaa4d 100644 --- a/src/gromacs/restraint/restraintpotential.h +++ b/src/gromacs/restraint/restraintpotential.h @@ -42,9 +42,16 @@ struct t_inputrec; struct t_mdatoms; struct t_pbc; +namespace gmxapi +{ +class Session; +} + namespace gmx { +class Mdrunner; // defined in src/programs/mdrun/runner.h + /*! * \brief Provide a vector type name with a more stable interface than RVec and a more stable * implementation than vec3<>. @@ -206,19 +213,57 @@ class IRestraintPotential double t) = 0; - // An update function to be called on the simulation master rank/thread periodically by the Restraint framework. + /*! + * \brief Call-back hook for restraint implementations. + * + * An update function to be called on the simulation master rank/thread periodically + * by the Restraint framework. + * Receives the same input as the evaluate() method, but is only called on the master + * rank of a simulation to allow implementation code to be thread-safe without knowing + * anything about the domain decomposition. + * + * \param v position of the first site + * \param v0 position of the second site + * \param t simulation time + * + * \internal + * We give the definition here because we don't want plugins to have to link against + * libgromacs right now (complicated header maintenance and no API stability guarantees). + * But once we've had plugin restraints wrap themselves in a Restraint template, we can set update = 0 + * \todo: Provide gmxapi facility for plugin restraints to wrap themselves with a default implementation to let this class be pure virtual. + */ virtual void update(gmx::Vector v, gmx::Vector v0, double t) { (void)v; (void)v0; (void)t; }; - // We give the definition here because we don't want plugins to have to link against libgromacs right now. + // We give the definition here because we don't want plugins to have to link against libgromacs right now (complicated header maintenance and no API stability guarantees). // But once we've had plugin restraints wrap themselves in a Restraint template, we can // set update = 0 + // Todo: Provide gmxapi facility for plugin restraints to wrap themselves with a default implementation to let this class be pure virtual. - virtual /*! + /*! * \brief Find out what sites this restraint is configured to act on. * \return */ + virtual std::vector sites() const = 0; + + /*! + * \brief Allow the Mdrunner for a simulation to interact with a module. + * + * A module implements this method to receive a handle to the runner that will be + * invoking the integrator to which the module is/will be attached. This allows the module + * to perform custom binding routines that require knowledge of or access to the runner. + * Other hooks include the force provider initialization and the restraint force calculation + * during the ForceProviders execution, which occur at lower levels. + * + * \param runner + */ + virtual void bindSession(gmxapi::Session* session) + { + // Defined in header as a temporary stop-gap to keep this interface purely public. + // Default: no-op. + (void) session; + } }; /*! diff --git a/src/programs/CMakeLists.txt b/src/programs/CMakeLists.txt index ebb030870..21df5277a 100644 --- a/src/programs/CMakeLists.txt +++ b/src/programs/CMakeLists.txt @@ -67,9 +67,9 @@ else() endif() add_library(view_objlib OBJECT ${VIEW_SOURCES}) add_executable(gmx - ${GMX_MAIN_SOURCES} - $ - $) + ${GMX_MAIN_SOURCES} + $ + $) target_link_libraries(gmx libgromacs ${GMX_COMMON_LIBRARIES} ${GMX_EXE_LINKER_FLAGS} diff --git a/src/programs/mdrun/context.cpp b/src/programs/mdrun/context.cpp new file mode 100644 index 000000000..572aaa197 --- /dev/null +++ b/src/programs/mdrun/context.cpp @@ -0,0 +1,28 @@ +// +// Created by Eric Irrgang on 5/16/18. +// + +#include "context.h" + +#include "gromacs/mdlib/simulationsignal.h" + +#include "runner.h" + +namespace gmx +{ +namespace md +{ + +Context::Context(const Mdrunner &runner) +{ + runner_ = &runner; +} + +SimulationSignals* Context::simulationSignals() const +{ + return runner_->signals(); +} + + +} // end namespace md +} // end namespace gmx diff --git a/src/programs/mdrun/context.h b/src/programs/mdrun/context.h new file mode 100644 index 000000000..d6d61ad26 --- /dev/null +++ b/src/programs/mdrun/context.h @@ -0,0 +1,57 @@ +// +// Created by Eric Irrgang on 5/16/18. +// + +#ifndef GROMACS_CONTEXT_H +#define GROMACS_CONTEXT_H + +// Ugh... avoiding this header dependency is as ugly as not... +#include "gromacs/mdlib/simulationsignal.h" + +namespace gmx +{ + +class Mdrunner; + +namespace md +{ + +/*! + * \brief Encapsulate some runtime context for sharing in the mdrun call stack. + * + * In the future, this functionality can be moved to an updated ProgramContext and + * the Context should only provide high-level or external information directly. Its + * primary purpose will be to register and hold factory function pointers with which + * callers can get handles to the resources they need. + * + * Since those modules and resources don't exist yet, we're providing a few directly. + */ +class Context { + public: + /*! + * \brief Construct with the runner's one resource: a pointer to the owning runner. + * + * The Context should be owned by a runner and the Context lifetime should be entirely + * within the Runner's life. + * + * \param runner non-owning pointer to the runner that owns the Context object. + */ + explicit Context(const Mdrunner &runner); + + /*! + * \brief Get a reference to the current array of signal flags. + * + * There is no guarantee that the flags have been initialized yet. + * + * \return pointer to signals array. + */ + SimulationSignals * simulationSignals() const; + + private: + const gmx::Mdrunner* runner_{nullptr}; +}; + +} // end namespace md +} // end namespace gmx + +#endif //GROMACS_CONTEXT_H diff --git a/src/programs/mdrun/md.cpp b/src/programs/mdrun/md.cpp index 399e433ec..162ea6506 100644 --- a/src/programs/mdrun/md.cpp +++ b/src/programs/mdrun/md.cpp @@ -128,6 +128,7 @@ #include "deform.h" #include "membed.h" #include "repl_ex.h" +#include "context.h" #ifdef GMX_FAHCORE #include "corewrap.h" @@ -236,7 +237,8 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, real cpt_period, real max_hours, int imdport, unsigned long Flags, - gmx_walltime_accounting_t walltime_accounting) + gmx_walltime_accounting_t walltime_accounting, + const gmx::md::Context& context) { gmx_mdoutf_t outf = nullptr; gmx_int64_t step, step_rel; @@ -310,7 +312,7 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, bool shouldCheckNumberOfBondedInteractions = false; int totalNumberOfBondedInteractions = -1; - SimulationSignals signals; + SimulationSignals& signals = *context.simulationSignals(); // Most global communnication stages don't propagate mdrun // signals, and will use this object to achieve that. SimulationSignaller nullSignaller(nullptr, nullptr, false, false); diff --git a/src/programs/mdrun/runner.cpp b/src/programs/mdrun/runner.cpp index 55f95dd69..c9ce918e4 100644 --- a/src/programs/mdrun/runner.cpp +++ b/src/programs/mdrun/runner.cpp @@ -131,6 +131,7 @@ #include "membed.h" #include "repl_ex.h" #include "resource-division.h" +#include "context.h" #ifdef GMX_FAHCORE #include "corewrap.h" @@ -1669,21 +1670,38 @@ int Mdrunner::mdrunner() Flags.test(ddBondCheck), fr->cginfo_mb); } + auto context = gmx::md::Context(*this); /* Now do whatever the user wants us to do (how flexible...) */ - my_integrator(inputrec->eI) (fplog, cr, mdlog, nfile, fnm, - oenv, bVerbose, - nstglobalcomm, - vsite, constr, - nstepout, mdModules.outputProvider(), - inputrec, mtop, - fcd, state, &observablesHistory, - mdatoms, nrnb, wcycle, fr, - replExParams, - membed, - cpt_period, max_hours, - imdport, - Flags.to_ulong(), - walltime_accounting); + auto integrator = my_integrator(inputrec->eI); + integrator(fplog, + cr, + mdlog, + nfile, + fnm, + oenv, + bVerbose, + nstglobalcomm, + vsite, + constr, + nstepout, + mdModules.outputProvider(), + inputrec, + mtop, + fcd, + state, + &observablesHistory, + mdatoms, + nrnb, + wcycle, + fr, + replExParams, + membed, + cpt_period, + max_hours, + imdport, + Flags.to_ulong(), + walltime_accounting, + context); if (inputrec->bRot) { @@ -1838,7 +1856,18 @@ void Mdrunner::addPullPotential(std::shared_ptr puller // When multiple restraints are used, it may be wasteful to register them separately. // Maybe instead register a Restraint Manager as a force provider. - restraintManager_->addSpec(std::move(puller), std::move(name)); + restraintManager_->addToSpec(std::move(puller), + std::move(name)); +} + +void Mdrunner::declareFinalStep() +{ + simulationSignals_[eglsSTOPCOND].sig = true; +} + +SimulationSignals *Mdrunner::signals() const +{ + return &simulationSignals_; } Mdrunner &Mdrunner::operator=(Mdrunner &&) = default; diff --git a/src/programs/mdrun/runner.h b/src/programs/mdrun/runner.h index fc1b6f500..d0f746234 100644 --- a/src/programs/mdrun/runner.h +++ b/src/programs/mdrun/runner.h @@ -57,6 +57,7 @@ #include "gromacs/hardware/hw_info.h" #include "gromacs/math/vec.h" #include "gromacs/mdlib/main.h" +#include "gromacs/mdlib/simulationsignal.h" #include "gromacs/mdtypes/imdmodule.h" #include "gromacs/topology/topology.h" #include "gromacs/utility/basedefinitions.h" @@ -210,6 +211,13 @@ class Mdrunner //! Handle to communication data structure. t_commrec *cr; + //! Allow override of final step from original input. + volatile bool finalStep_{false}; + + //! hold the signaling object since integrator_t is just a function. + //! Signals are always mutable. + mutable SimulationSignals simulationSignals_; + std::shared_ptr restraintManager_{nullptr}; std::shared_ptr tpxState_{nullptr}; @@ -279,6 +287,31 @@ class Mdrunner //! Called when thread-MPI spawns threads. t_commrec *spawnThreads(int numThreadsToLaunch); + /*! + * \brief API hook to allow a toggle to end the simulation. + * + * Sets a simulation stop condition. The + * next pass through the MD loop exits cleanly. Allows a client of the Mdrunner + * to cleanly interrupt a integrator_t function during execution of an MD loop. + * + * Note that the runner does not know the current step of the executing MD loop. + * It just owns the data resource that the integrator will check. The calling code + * should bear this in mind. + * + * May be called on any or all ranks. + */ + void declareFinalStep(); + + /*! + * \brief Get raw pointer to the simulation signals mediated by the runner. + * + * The array pointed to is guaranteed to exist, but the elements may not yet be + * initialized. + * + * \return pointer to array of Signal objects. + */ + SimulationSignals* signals() const; + /*! \brief Initializes a new Mdrunner from the master. * * Run in a new thread from a const pointer to the master.