Redesign GPU FFT abstraction
[alexxy/gromacs.git] / src / gromacs / utility / binaryinformation.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 /*! \internal \file
39  * \brief Implements functionality for printing information about the
40  * currently running binary
41  *
42  * \ingroup module_utility
43  */
44 #include "gmxpre.h"
45
46 #include "binaryinformation.h"
47
48 #include "config.h"
49
50 #if GMX_FFT_FFTW3 || GMX_FFT_ARMPL_FFTW3
51 // Needed for construction of the FFT library description string
52 #    include <fftw3.h>
53 #endif
54
55 #ifdef HAVE_LIBMKL
56 #    include <mkl.h>
57 #endif
58
59 #if HAVE_EXTRAE
60 #    include <extrae_user_events.h>
61 #endif
62
63 #if GMX_USE_HWLOC
64 #    include <hwloc.h>
65 #endif
66
67 #include <cstdio>
68 #include <cstdlib>
69 #include <cstring>
70
71 #include <algorithm>
72 #include <array>
73 #include <string>
74
75 /* This file is completely threadsafe - keep it that way! */
76
77 #include "buildinfo.h"
78 #include "gromacs/utility/arraysize.h"
79 #include "gromacs/utility/baseversion.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/gmxassert.h"
82 #include "gromacs/utility/path.h"
83 #include "gromacs/utility/programcontext.h"
84 #include "gromacs/utility/stringutil.h"
85 #include "gromacs/utility/sysinfo.h"
86 #include "gromacs/utility/textwriter.h"
87
88 #include "cuda_version_information.h"
89
90 namespace
91 {
92
93 using gmx::formatString;
94
95 //! \cond Doxygen does not need to care about most of this stuff, and the macro usage is painful to document
96
97 int centeringOffset(int width, int length)
98 {
99     return std::max(width - length, 0) / 2;
100 }
101
102 std::string formatCentered(int width, const char* text)
103 {
104     const int offset = centeringOffset(width, std::strlen(text));
105     return formatString("%*s%s", offset, "", text);
106 }
107
108 void printCopyright(gmx::TextWriter* writer)
109 {
110     // Contributors sorted alphabetically by last name
111     static const char* const Contributors[]  = { "Andrey Alekseenko",
112                                                 "Emile Apol",
113                                                 "Rossen Apostolov",
114                                                 "Paul Bauer",
115                                                 "Herman J.C. Berendsen",
116                                                 "Par Bjelkmar",
117                                                 "Christian Blau",
118                                                 "Viacheslav Bolnykh",
119                                                 "Kevin Boyd",
120                                                 "Aldert van Buuren",
121                                                 "Rudi van Drunen",
122                                                 "Anton Feenstra",
123                                                 "Gaurav Garg",
124                                                 "Gilles Gouaillardet",
125                                                 "Alan Gray",
126                                                 "Gerrit Groenhof",
127                                                 "Anca Hamuraru",
128                                                 "Vincent Hindriksen",
129                                                 "M. Eric Irrgang",
130                                                 "Aleksei Iupinov",
131                                                 "Christoph Junghans",
132                                                 "Joe Jordan",
133                                                 "Dimitrios Karkoulis",
134                                                 "Peter Kasson",
135                                                 "Jiri Kraus",
136                                                 "Carsten Kutzner",
137                                                 "Per Larsson",
138                                                 "Justin A. Lemkul",
139                                                 "Viveca Lindahl",
140                                                 "Magnus Lundborg",
141                                                 "Erik Marklund",
142                                                 "Pascal Merz",
143                                                 "Pieter Meulenhoff",
144                                                 "Teemu Murtola",
145                                                 "Szilard Pall",
146                                                 "Sander Pronk",
147                                                 "Roland Schulz",
148                                                 "Michael Shirts",
149                                                 "Alexey Shvetsov",
150                                                 "Alfons Sijbers",
151                                                 "Peter Tieleman",
152                                                 "Jon Vincent",
153                                                 "Teemu Virolainen",
154                                                 "Christian Wennberg",
155                                                 "Maarten Wolf",
156                                                 "Artem Zhmurov" };
157     static const char* const CopyrightText[] = {
158         "Copyright (c) 1991-2000, University of Groningen, The Netherlands.",
159         "Copyright (c) 2001-2019, The GROMACS development team at",
160         "Uppsala University, Stockholm University and",
161         "the Royal Institute of Technology, Sweden.",
162         "check out http://www.gromacs.org for more information."
163     };
164
165 #define NCONTRIBUTORS static_cast<int>(asize(Contributors))
166 #define NCR static_cast<int>(asize(CopyrightText))
167
168     // TODO a centering behaviour of TextWriter could be useful here
169     writer->writeLine(formatCentered(78, "GROMACS is written by:"));
170     for (int i = 0; i < NCONTRIBUTORS;)
171     {
172         for (int j = 0; j < 3 && i < NCONTRIBUTORS; ++j, ++i)
173         {
174             const int            width = 26;
175             std::array<char, 30> buf;
176             const int            offset = centeringOffset(width, strlen(Contributors[i]));
177             GMX_RELEASE_ASSERT(static_cast<int>(strlen(Contributors[i])) + offset < gmx::ssize(buf),
178                                "Formatting buffer is not long enough");
179             std::fill(buf.begin(), buf.begin() + offset, ' ');
180             std::strncpy(buf.data() + offset, Contributors[i], gmx::ssize(buf) - offset);
181             writer->writeString(formatString(" %-*s", width, buf.data()));
182         }
183         writer->ensureLineBreak();
184     }
185     writer->writeLine(formatCentered(78, "and the project leaders:"));
186     writer->writeLine(
187             formatCentered(78, "Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel"));
188     writer->ensureEmptyLine();
189     for (int i = 0; i < NCR; ++i)
190     {
191         writer->writeLine(CopyrightText[i]);
192     }
193     writer->ensureEmptyLine();
194
195     // Folding At Home has different licence to allow digital
196     // signatures in GROMACS, so does not need to show the normal
197     // license statement.
198     if (!GMX_FAHCORE)
199     {
200         writer->writeLine("GROMACS is free software; you can redistribute it and/or modify it");
201         writer->writeLine("under the terms of the GNU Lesser General Public License");
202         writer->writeLine("as published by the Free Software Foundation; either version 2.1");
203         writer->writeLine("of the License, or (at your option) any later version.");
204     }
205 }
206
207 //! Construct a string that describes the library that provides CPU FFT support to this build
208 const char* getCpuFftDescriptionString()
209 {
210 // Define the FFT description string
211 #if GMX_FFT_FFTW3 || GMX_FFT_ARMPL_FFTW3
212 #    if GMX_NATIVE_WINDOWS
213     // Don't buy trouble
214     return "fftw3";
215 #    else
216     // Use the version string provided by libfftw3
217 #        if GMX_DOUBLE
218     return fftw_version;
219 #        else
220     return fftwf_version;
221 #        endif
222 #    endif
223 #endif
224 #if GMX_FFT_MKL
225     return "Intel MKL";
226 #endif
227 #if GMX_FFT_FFTPACK
228     return "fftpack (built-in)";
229 #endif
230 };
231
232 //! Construct a string that describes the library that provides GPU FFT support to this build
233 const char* getGpuFftDescriptionString()
234 {
235     if (GMX_GPU)
236     {
237         if (GMX_GPU_CUDA)
238         {
239             return "cuFFT";
240         }
241         else if (GMX_GPU_OPENCL)
242         {
243             return "clFFT";
244         }
245         else if (GMX_GPU_SYCL)
246         {
247             return "unknown";
248         }
249         else
250         {
251             GMX_RELEASE_ASSERT(false, "Unknown GPU configuration");
252             return "impossible";
253         }
254     }
255     else
256     {
257         return "none";
258     }
259 };
260
261 void gmx_print_version_info(gmx::TextWriter* writer)
262 {
263     writer->writeLine(formatString("GROMACS version:    %s", gmx_version()));
264     const char* const git_hash = gmx_version_git_full_hash();
265     if (git_hash[0] != '\0')
266     {
267         writer->writeLine(formatString("GIT SHA1 hash:      %s", git_hash));
268     }
269     const char* const base_hash = gmx_version_git_central_base_hash();
270     if (base_hash[0] != '\0')
271     {
272         writer->writeLine(formatString("Branched from:      %s", base_hash));
273     }
274     const char* const releaseSourceChecksum = gmxReleaseSourceChecksum();
275     const char* const currentSourceChecksum = gmxCurrentSourceChecksum();
276     if (releaseSourceChecksum[0] != '\0')
277     {
278         if (std::strcmp(releaseSourceChecksum, "NoChecksumFile") == 0)
279         {
280             writer->writeLine(formatString(
281                     "The source code this program was compiled from has not been verified because "
282                     "the reference checksum was missing during compilation. This means you have an "
283                     "incomplete GROMACS distribution, please make sure to download an intact "
284                     "source distribution and compile that before proceeding."));
285             writer->writeLine(formatString("Computed checksum: %s", currentSourceChecksum));
286         }
287         else if (std::strcmp(releaseSourceChecksum, "NoPythonAvailable") == 0)
288         {
289             writer->writeLine(
290                     formatString("Build source could not be verified, because the checksum could "
291                                  "not be computed."));
292         }
293         else if (std::strcmp(releaseSourceChecksum, currentSourceChecksum) != 0)
294         {
295             writer->writeLine(formatString(
296                     "This program has been built from source code that has been altered and does "
297                     "not match the code released as part of the official GROMACS version %s. If "
298                     "you did not intend to use an altered GROMACS version, make sure to download "
299                     "an intact source distribution and compile that before proceeding.",
300                     gmx_version()));
301             writer->writeLine(formatString(
302                     "If you have modified the source code, you are strongly encouraged to set your "
303                     "custom version suffix (using -DGMX_VERSION_STRING_OF_FORK) which will can "
304                     "help later with scientific reproducibility but also when reporting bugs."));
305             writer->writeLine(formatString("Release checksum: %s", releaseSourceChecksum));
306             writer->writeLine(formatString("Computed checksum: %s", currentSourceChecksum));
307         }
308         else
309         {
310             writer->writeLine(formatString("Verified release checksum is %s", releaseSourceChecksum));
311         }
312     }
313
314
315 #if GMX_DOUBLE
316     writer->writeLine("Precision:          double");
317 #else
318     writer->writeLine("Precision:          mixed");
319 #endif
320     writer->writeLine(formatString("Memory model:       %u bit", static_cast<unsigned>(8 * sizeof(void*))));
321
322 #if GMX_THREAD_MPI
323     writer->writeLine("MPI library:        thread_mpi");
324 #elif GMX_MPI
325 #    if HAVE_CUDA_AWARE_MPI
326     writer->writeLine("MPI library:        MPI (CUDA-aware)");
327 #    else
328     writer->writeLine("MPI library:        MPI");
329 #    endif
330 #else
331     writer->writeLine("MPI library:        none");
332 #endif
333 #if GMX_OPENMP
334     writer->writeLine(formatString("OpenMP support:     enabled (GMX_OPENMP_MAX_THREADS = %d)",
335                                    GMX_OPENMP_MAX_THREADS));
336 #else
337     writer->writeLine("OpenMP support:     disabled");
338 #endif
339     writer->writeLine(formatString("GPU support:        %s", getGpuImplementationString()));
340     writer->writeLine(formatString("SIMD instructions:  %s", GMX_SIMD_STRING));
341     writer->writeLine(formatString("CPU FFT library:    %s", getCpuFftDescriptionString()));
342     writer->writeLine(formatString("GPU FFT library:    %s", getGpuFftDescriptionString()));
343 #if GMX_TARGET_X86
344     writer->writeLine(formatString("RDTSCP usage:       %s", GMX_USE_RDTSCP ? "enabled" : "disabled"));
345 #endif
346 #if GMX_USE_TNG
347     writer->writeLine("TNG support:        enabled");
348 #else
349     writer->writeLine("TNG support:        disabled");
350 #endif
351 #if GMX_USE_HWLOC
352     writer->writeLine(formatString("Hwloc support:      hwloc-%s", HWLOC_VERSION));
353 #else
354     writer->writeLine("Hwloc support:      disabled");
355 #endif
356 #if HAVE_EXTRAE
357     unsigned major, minor, revision;
358     Extrae_get_version(&major, &minor, &revision);
359     writer->writeLine(formatString(
360             "Tracing support:    enabled. Using Extrae-%d.%d.%d", major, minor, revision));
361 #else
362     writer->writeLine("Tracing support:    disabled");
363 #endif
364
365
366     /* TODO: The below strings can be quite long, so it would be nice to wrap
367      * them. Can wait for later, as the master branch has ready code to do all
368      * that. */
369     writer->writeLine(formatString("C compiler:         %s", BUILD_C_COMPILER));
370     writer->writeLine(formatString(
371             "C compiler flags:   %s %s", BUILD_CFLAGS, CMAKE_BUILD_CONFIGURATION_C_FLAGS));
372     writer->writeLine(formatString("C++ compiler:       %s", BUILD_CXX_COMPILER));
373     writer->writeLine(formatString(
374             "C++ compiler flags: %s %s", BUILD_CXXFLAGS, CMAKE_BUILD_CONFIGURATION_CXX_FLAGS));
375 #ifdef HAVE_LIBMKL
376     /* MKL might be used for LAPACK/BLAS even if FFTs use FFTW, so keep it separate */
377     writer->writeLine(formatString(
378             "Linked with Intel MKL version %d.%d.%d.", __INTEL_MKL__, __INTEL_MKL_MINOR__, __INTEL_MKL_UPDATE__));
379 #endif
380 #if GMX_GPU_OPENCL
381     writer->writeLine(formatString("OpenCL include dir: %s", OPENCL_INCLUDE_DIR));
382     writer->writeLine(formatString("OpenCL library:     %s", OPENCL_LIBRARY));
383     writer->writeLine(formatString("OpenCL version:     %s", OPENCL_VERSION_STRING));
384 #endif
385 #if GMX_GPU_CUDA
386     writer->writeLine(formatString("CUDA compiler:      %s", CUDA_COMPILER_INFO));
387     writer->writeLine(formatString(
388             "CUDA compiler flags:%s %s", CUDA_COMPILER_FLAGS, CMAKE_BUILD_CONFIGURATION_CXX_FLAGS));
389     writer->writeLine("CUDA driver:        " + gmx::getCudaDriverVersionString());
390     writer->writeLine("CUDA runtime:       " + gmx::getCudaRuntimeVersionString());
391 #endif
392 #if GMX_SYCL_DPCPP
393     writer->writeLine(formatString("SYCL DPCPP flags:   %s", SYCL_DPCPP_COMPILER_FLAGS));
394 #endif
395 #if GMX_SYCL_HIPSYCL
396     writer->writeLine(formatString("hipSYCL launcher:   %s", SYCL_HIPSYCL_COMPILER_LAUNCHER));
397     writer->writeLine(formatString("hipSYCL flags:      %s", SYCL_HIPSYCL_COMPILER_FLAGS));
398     writer->writeLine(formatString("hipSYCL platforms:  %s", SYCL_HIPSYCL_PLATFORMS));
399 #endif
400 }
401
402 //! \endcond
403
404 } // namespace
405
406 namespace gmx
407 {
408
409 BinaryInformationSettings::BinaryInformationSettings() :
410     bExtendedInfo_(false),
411     bCopyright_(false),
412     bProcessId_(false),
413     bGeneratedByHeader_(false),
414     prefix_(""),
415     suffix_("")
416 {
417 }
418
419 void printBinaryInformation(FILE* fp, const IProgramContext& programContext)
420 {
421     TextWriter writer(fp);
422     printBinaryInformation(&writer, programContext, BinaryInformationSettings());
423 }
424
425 void printBinaryInformation(FILE*                            fp,
426                             const IProgramContext&           programContext,
427                             const BinaryInformationSettings& settings)
428 {
429     try
430     {
431         TextWriter writer(fp);
432         printBinaryInformation(&writer, programContext, settings);
433     }
434     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
435 }
436
437 void printBinaryInformation(TextWriter*                      writer,
438                             const IProgramContext&           programContext,
439                             const BinaryInformationSettings& settings)
440 {
441     // TODO Perhaps the writer could be configured with the prefix and
442     // suffix strings from the settings?
443     const char* prefix          = settings.prefix_;
444     const char* suffix          = settings.suffix_;
445     const char* precisionString = "";
446 #if GMX_DOUBLE
447     precisionString = " (double precision)";
448 #endif
449     const char* const name = programContext.displayName();
450     if (settings.bGeneratedByHeader_)
451     {
452         writer->writeLine(formatString("%sCreated by:%s", prefix, suffix));
453     }
454     // TODO: It would be nice to know here whether we are really running a
455     // Gromacs binary or some other binary that is calling Gromacs; we
456     // could then print "%s is part of GROMACS" or some alternative text.
457     std::string title = formatString(":-) GROMACS - %s, %s%s (-:", name, gmx_version(), precisionString);
458     const int indent =
459             centeringOffset(78 - std::strlen(prefix) - std::strlen(suffix), title.length()) + 1;
460     writer->writeLine(formatString("%s%*c%s%s", prefix, indent, ' ', title.c_str(), suffix));
461     writer->writeLine(formatString("%s%s", prefix, suffix));
462     if (settings.bCopyright_)
463     {
464         GMX_RELEASE_ASSERT(prefix[0] == '\0' && suffix[0] == '\0',
465                            "Prefix/suffix not supported with copyright");
466         printCopyright(writer);
467         writer->ensureEmptyLine();
468         // This line is printed again after the copyright notice to make it
469         // appear together with all the other information, so that it is not
470         // necessary to read stuff above the copyright notice.
471         // The line above the copyright notice puts the copyright notice is
472         // context, though.
473         writer->writeLine(formatString(
474                 "%sGROMACS:      %s, version %s%s%s", prefix, name, gmx_version(), precisionString, suffix));
475     }
476     const char* const binaryPath = programContext.fullBinaryPath();
477     if (!gmx::isNullOrEmpty(binaryPath))
478     {
479         writer->writeLine(formatString("%sExecutable:   %s%s", prefix, binaryPath, suffix));
480     }
481     const gmx::InstallationPrefixInfo installPrefix = programContext.installationPrefix();
482     if (!gmx::isNullOrEmpty(installPrefix.path))
483     {
484         writer->writeLine(formatString("%sData prefix:  %s%s%s",
485                                        prefix,
486                                        installPrefix.path,
487                                        installPrefix.bSourceLayout ? " (source tree)" : "",
488                                        suffix));
489     }
490     const std::string workingDir = Path::getWorkingDirectory();
491     if (!workingDir.empty())
492     {
493         writer->writeLine(formatString("%sWorking dir:  %s%s", prefix, workingDir.c_str(), suffix));
494     }
495     if (settings.bProcessId_)
496     {
497         writer->writeLine(formatString("%sProcess ID:   %d%s", prefix, gmx_getpid(), suffix));
498     }
499     const char* const commandLine = programContext.commandLine();
500     if (!gmx::isNullOrEmpty(commandLine))
501     {
502         writer->writeLine(formatString(
503                 "%sCommand line:%s\n%s  %s%s", prefix, suffix, prefix, commandLine, suffix));
504     }
505     if (settings.bExtendedInfo_)
506     {
507         GMX_RELEASE_ASSERT(prefix[0] == '\0' && suffix[0] == '\0',
508                            "Prefix/suffix not supported with extended info");
509         writer->ensureEmptyLine();
510         gmx_print_version_info(writer);
511     }
512 }
513
514 } // namespace gmx