Remove logging from hardware detection
[alexxy/gromacs.git] / api / gmxapi / cpp / context_impl.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #ifndef GMXAPI_CONTEXT_IMPL_H
36 #define GMXAPI_CONTEXT_IMPL_H
37 /*! \file
38  * \brief Declare gmxapi::ContextImpl
39  *
40  * \author M. Eric Irrgang <ericirrgang@gmail.com>
41  * \ingroup gmxapi
42  */
43
44 #include <memory>
45 #include <string>
46
47 #include "gromacs/mdrun/legacymdrunoptions.h"
48 #include "gromacs/mdtypes/mdrunoptions.h"
49 #include "gromacs/utility/gmxmpi.h"
50
51 #include "gmxapi/context.h"
52 #include "gmxapi/session.h"
53
54 struct gmx_hw_info_t;
55
56 namespace gmxapi
57 {
58
59 /*!
60  * \brief Provide RAII management of library MPI context.
61  *
62  * To acquire an MpiContextManager is to have assurance that any external MPI
63  * environment is ready to use. When the MpiContextManager is released or
64  * goes out of scope, the destructor finalizes the resources.
65  *
66  * It is plausible that one or more ranks may not participate in an API session,
67  * but we have not historically handled such use cases at this level.
68  * For GROMACS built with an MPI library, the root communicator in the MpiContextManager
69  * must be valid. For other builds (e.g. tMPI), the communicator member must be MPI_COMM_NULL.
70  *
71  * If the client provides a communicator, the client promises to maintain the validity
72  * of the communicator for the life of the MpiContextManager.
73  *
74  * Note that thread-MPI chooses the number of ranks and constructs its
75  * MPI communicator internally, so does not and is unlikely to ever
76  * participate here.
77  *
78  * \todo Log errors during set up or tear down.
79  * There is no resource for logging or reporting errors during initialization or when misused.
80  *
81  * \ingroup gmxapi
82  */
83 class MpiContextManager
84 {
85 public:
86     /*!
87      * \brief Default constructor.
88      *
89      * Construct a valid instance with an appropriate default value for the
90      * base communicator. (Note that appropriate default value depends on whether
91      * the library was compiled with an external MPI library.)
92      *
93      * \throws BasicException if library cannot be initialized.
94      */
95     MpiContextManager();
96
97     /*!
98      * \brief Allow the communicator to be specified.
99      *
100      * Supports use cases in which a client provides a non-default communicator.
101      * Ensures that library environment is properly initialized and finalized,
102      * whether or not an externally managed communicator has been provided.
103      *
104      * \param communicator Optional communicator representing a client-managed MPI environment.
105      *
106      * Note that the communicator must be MPI_COMM_NULL if and only if GROMACS was built without an
107      * external MPI library.
108      *
109      * \throws BasicException if library cannot be initialized.
110      *
111      * \todo (#3650?) Decide whether to exclude this from tMPI environments or find a sensible invariant.
112      */
113     explicit MpiContextManager(MPI_Comm communicator);
114
115     ~MpiContextManager();
116
117     /*!
118      * \brief Exclusive ownership of a scoped context means copying is impossible.
119      *
120      * \{
121      */
122     MpiContextManager(const MpiContextManager&) = delete;
123     MpiContextManager& operator=(const MpiContextManager&) = delete;
124     //! \}
125
126     /*!
127      * \brief Transfer ownership of the managed GROMACS MPI context.
128      *
129      * \{
130      */
131     MpiContextManager(MpiContextManager&& source) noexcept = default;
132     MpiContextManager& operator=(MpiContextManager&& source) noexcept = default;
133     //! \}
134
135     /*!
136      * \brief Get the communicator for this context.
137      *
138      * \return Communicator with an appropriate initialized state for the current library
139      * configuration.
140      *
141      * \throws gmxapi::UsageError if MpiContextManager is in an invalid state, such as after
142      *  being moved from.
143      */
144     [[nodiscard]] MPI_Comm communicator() const;
145
146 private:
147     /*!
148      * \brief The resources provided by this manager.
149      *
150      * A valid MpiContextManager has a value for communicator_ that is appropriate
151      * for the library configuration.
152      */
153     std::unique_ptr<MPI_Comm> communicator_;
154 };
155
156 /*!
157  * \brief Context implementation.
158  *
159  * Execution contexts have a uniform interface specified by the API. Implementations for
160  * particular execution environments can specialize / derive from this base.
161  *
162  * \todo Separate interface and implementation.
163  *
164  * \warning Definition and semantics depend on configure-time details. For example,
165  *          MPI-enabled libraries always hold a valid MPI communicator via MpiContextManager,
166  *          whereas tMPI and non-MPI builds hold a meaningless MpiContextManager.
167  *
168  * \todo Provide functions or traits for introspection.
169  *
170  * \ingroup gmxapi
171  */
172 class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
173 {
174 public:
175     ~ContextImpl();
176
177     /*!
178      * \brief Factory function
179      *
180      * Since this class provides `shared_from_this`, we need to make sure
181      * that it never exists without a shared_ptr owning it.
182      *
183      * If we can confirm `shared_from_this` is no longer necessary, implementation may change.
184      * \todo: Use registration/deregistration of launched Sessions to log warnings on shutdown
185      *        instead of letting Session keep ContextImpl alive.
186      *
187      * \return ownership of a new object
188      */
189     static std::shared_ptr<ContextImpl> create(MpiContextManager&& mpi);
190
191     /*!
192      * \brief Copy disallowed because Session state would become ambiguous.
193      *
194      * The API implementation needs to unambiguously determine
195      * which Sessions and Contexts are associated with each other.
196      * \{
197      */
198     ContextImpl(const ContextImpl&) = delete;
199     ContextImpl& operator=(const ContextImpl&) = delete;
200     //! \}
201
202     /*!
203      * \brief Objects are not trivial to move.
204      *
205      * \todo Implement move semantics. Requires a moveable const MpiContextManager and
206      *       LegacyMdrunOptions members.
207      *
208      * \{
209      */
210     ContextImpl(ContextImpl&&) = delete;
211     ContextImpl& operator=(ContextImpl&&) = delete;
212     //! \}
213
214     /*!
215      * \brief Translate the workflow to the execution context and launch.
216      *
217      * \param work workflow graph
218      * \return ownership of a new session
219      *
220      * \todo This probably makes more sense as a free function, but we need to determine access policies.
221      *
222      * Session is returned with shared_ptr ownership so that Context
223      * can hold a weak_ptr and because Session Resources handles
224      * are still evolving.
225      * \todo Hide lifetime management and ownership from handle object.
226      * We can achieve the necessary aspects of this shared_ptr at a lower level of implementation.
227      * Note also that returned value policies can be implemented in higher level wrappers to ensure
228      * correct object lifetime scope. (See pybind, for instance.)
229      */
230     std::shared_ptr<Session> launch(const Workflow& work);
231
232     /*!
233      * \brief Retain the ability to find a launched session while it exists.
234      *
235      * The client owns the Session launched by a Context, but it is helpful
236      * for the Context to know if it has an active Session associated with it.
237      *
238      * \todo Use registration/deregistration protocol instead.
239      *       Requires logging facility.
240      */
241     std::weak_ptr<Session> session_;
242
243     /*!
244      * \brief mdrun command line arguments.
245      *
246      * Store arguments provided by the client and pass them when launching
247      * a simulation runner. This allows client code to access the same
248      * options as are available to mdrun on the command line while the API
249      * evolves.
250      */
251     MDArgs mdArgs_;
252
253     /*!
254      * \brief Legacy option-handling and set up for mdrun.
255      *
256      * This object should not exist, but is necessary now to introduce
257      * the API in a way that means CLI and API work similarly and do not
258      * duplicate definitions e.g. of command-line options.
259      */
260     gmx::LegacyMdrunOptions options_;
261
262     /*!
263      * \brief Scoped MPI management.
264      *
265      * Part of the ContextImpl invariant establishes a point where MPI initialization status is
266      * known.
267      *
268      * Note that the MpiContextManager invariant is dependent on configure-time options to the
269      * GROMACS library. Specifically, MpiContextManager::communicator() is guaranteed to be
270      * MPI_COMM_NULL if and only if the library was built without an external MPI library.
271      * This is confusing and we should split either this class or the
272      * MpiContextManager class. Another alternative would be to use a std::optional here and to
273      * allow the std::unique_ptr<MpiContextManager> provided to ContextImpl::create() to be null,
274      * but that means a slight change of documented behavior protocol. See #3688 and #3650 for
275      * follow up.
276      *
277      * To ensure the MpiContextManager is initialized only once, we use a const member that must
278      * be initialized at construction.
279      */
280     const MpiContextManager mpi_;
281
282     /*! \brief Owning handle to the results of the hardware detection.
283      *
284      * The hardware is detected across the whole environment described
285      * by \c mpi_ */
286     std::unique_ptr<gmx_hw_info_t> hardwareInformation_;
287
288 private:
289     /*!
290      * \brief Basic constructor.
291      *
292      * Don't use this. Use create() to get a shared pointer right away.
293      * Otherwise, shared_from_this() is potentially dangerous.
294      */
295     explicit ContextImpl(MpiContextManager&& mpi) noexcept(std::is_nothrow_constructible_v<gmx::LegacyMdrunOptions>);
296 };
297
298 /*!
299  * \brief Allow client code to hold a library communicator.
300  *
301  * MPI-enabled client code and thread-MPI GROMACS libraries cannot share headers
302  * in which MPI symbols (such as MPI_Comm) are defined. This class allows a
303  * client to refer to a library communicator opaquely. See gmxapi::offerComm().
304  *
305  * In the initial gmxapi::ResourceAssignment implementation, CommHandle objects
306  * are the only possible recipients of assigned resources. If future implementations
307  * allow richer resource assignment, CommHandle may be superseded by a more
308  * elaborate interface. Otherwise, all we need is this simple type proxy.
309  */
310 class CommHandle
311 {
312 public:
313     using commType = MPI_Comm;
314     MPI_Comm communicator{ MPI_COMM_NULL };
315 };
316
317 } // end namespace gmxapi
318 #endif // GMXAPI_CONTEXT_IMPL_H